<!DOCTYPE html>


  <html class="dark page-post">


<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  
  <title>webpack4定制前端开发环境 | Poetry&#39;s Blog</title>

  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  
    <meta name="keywords" content="webpack,">
  

  <meta name="description" content="来源掘金小册笔记  一、webpack概念和基础使用1.1 安装和使用npm install webpack webpack-cli -g # 或者yarn global add webpack webpack-cli# 然后就可以全局执行命令了webpack --help 1.2 webpack 的基本概念 webpack 本质上是一个打包工具，它会根据代码的内容解析模块依赖，帮助我们把多个模">
<meta name="keywords" content="webpack">
<meta property="og:type" content="article">
<meta property="og:title" content="webpack4定制前端开发环境">
<meta property="og:url" content="http://blog.poetries.top/2018/09/04/webpack-custom-work-flow/index.html">
<meta property="og:site_name" content="Poetry&#39;s Blog">
<meta property="og:description" content="来源掘金小册笔记  一、webpack概念和基础使用1.1 安装和使用npm install webpack webpack-cli -g # 或者yarn global add webpack webpack-cli# 然后就可以全局执行命令了webpack --help 1.2 webpack 的基本概念 webpack 本质上是一个打包工具，它会根据代码的内容解析模块依赖，帮助我们把多个模">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://poetries1.gitee.io/img-repo/2019/10/638.png">
<meta property="og:image" content="https://upload-images.jianshu.io/upload_images/1480597-ae36b6816feed422.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240">
<meta property="og:updated_time" content="2020-08-15T04:25:31.942Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="webpack4定制前端开发环境">
<meta name="twitter:description" content="来源掘金小册笔记  一、webpack概念和基础使用1.1 安装和使用npm install webpack webpack-cli -g # 或者yarn global add webpack webpack-cli# 然后就可以全局执行命令了webpack --help 1.2 webpack 的基本概念 webpack 本质上是一个打包工具，它会根据代码的内容解析模块依赖，帮助我们把多个模">
<meta name="twitter:image" content="https://poetries1.gitee.io/img-repo/2019/10/638.png">

  

  
    <link rel="icon" href="/favicon.ico">
  

  <link href="/css/styles.css?v=c114cbeddx" rel="stylesheet">
<link href="/css/other.css?v=c114cbeddx" rel="stylesheet">


  
    <link rel="stylesheet" href="/css/personal-style.css">
  

  

  
  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?40b1f89aa80f2527b3db779c6898c879";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>


  
  <script type="text/javascript">
	(function(){
	    var bp = document.createElement('script');
	    var curProtocol = window.location.protocol.split(':')[0];
	    if (curProtocol === 'https') {
	        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
	    }
	    else {
	        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
	    }
	    var s = document.getElementsByTagName("script")[0];
	    s.parentNode.insertBefore(bp, s);
	})();
  </script>



  
    <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css">
  

  <!-- 聊天系统 -->
  
    
   <link type="text/css" rel="stylesheet" href="/renxi/default.css">
   <style>
      #modal {
        position: static !important;
      }
      .filter {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
        background: #fe5757;
        animation: colorChange 30s ease-in-out infinite;
        animation-fill-mode: both;
        mix-blend-mode: overlay;
      }
  
      @keyframes colorChange {
        0%, 100% {
            opacity: 0;
        }
        50% {
            opacity: .9;
        }
      }
   </style>
</head>
</html>
<body>
  
  
    <span id="toolbox-mobile" class="toolbox-mobile">导航</span>
  

  <div class="post-header CENTER">
   
  <div class="toolbox">
    <a class="toolbox-entry" href="/">
      <span class="toolbox-entry-text">导航</span>
      <i class="icon-angle-down"></i>
      <i class="icon-home"></i>
    </a>
    <ul class="list-toolbox">
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/archives/"
            rel="noopener noreferrer"
            target="_self"
            >
            博客
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/categories/"
            rel="noopener noreferrer"
            target="_self"
            >
            分类
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/tags/"
            rel="noopener noreferrer"
            target="_self"
            >
            标签
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/search/"
            rel="noopener noreferrer"
            target="_self"
            >
            搜索
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/link/"
            rel="noopener noreferrer"
            target="_self"
            >
            友链
          </a>
        </li>
      
        <li class="item-toolbox">
          <a
            class="CIRCLE"
            href="/about/"
            rel="noopener noreferrer"
            target="_self"
            >
            关于
          </a>
        </li>
      
    </ul>
  </div>


</div>


  <div id="toc" class="toc-article">
    <strong class="toc-title">文章目录<i class="iconfont toc-title" style="display:inline-block;color:#87998d;width:20px;height:20px;">&#xf004b;</i></strong>
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#一、webpack概念和基础使用"><span class="toc-text">一、webpack概念和基础使用</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-1-安装和使用"><span class="toc-text">1.1 安装和使用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#1-2-webpack-的基本概念"><span class="toc-text">1.2 webpack 的基本概念</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#二、搭建基础的前端开发环境"><span class="toc-text">二、搭建基础的前端开发环境</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-关联-HTML"><span class="toc-text">2.1 关联 HTML</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-构建-CSS"><span class="toc-text">2.2 构建 CSS</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-3-CSS-预处理器"><span class="toc-text">2.3 CSS 预处理器</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-4-处理图片文件"><span class="toc-text">2.4 处理图片文件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-5-使用-Babel"><span class="toc-text">2.5 使用 Babel</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-6-启动静态服务"><span class="toc-text">2.6 启动静态服务</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-7-完整示例代码"><span class="toc-text">2.7 完整示例代码</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#三、webpack如何解析代码模块路径"><span class="toc-text">三、webpack如何解析代码模块路径</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#3-1-常用的一些配置"><span class="toc-text">3.1 常用的一些配置</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#四、配置loader"><span class="toc-text">四、配置loader</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#4-1-loader-匹配规则"><span class="toc-text">4.1 loader 匹配规则</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-2-规则条件配置"><span class="toc-text">4.2 规则条件配置</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-3-使用-loader-配置"><span class="toc-text">4.3 使用 loader 配置</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-4-loader-应用顺序"><span class="toc-text">4.4 loader 应用顺序</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-5-完整代码"><span class="toc-text">4.5 完整代码</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#五、使用plugin"><span class="toc-text">五、使用plugin</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#5-1-DefinePlugin"><span class="toc-text">5.1 DefinePlugin</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-2-copy-webpack-plugin"><span class="toc-text">5.2 copy-webpack-plugin</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-3-extract-text-webpack-plugin"><span class="toc-text">5.3 extract-text-webpack-plugin</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#六、更好使用webpack-dev-server"><span class="toc-text">六、更好使用webpack-dev-server</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#6-1-基础使用"><span class="toc-text">6.1 基础使用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-2-配置"><span class="toc-text">6.2 配置</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#七、开发和生产环境的构建配置差异"><span class="toc-text">七、开发和生产环境的构建配置差异</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#7-1-在配置文件中区分-mode"><span class="toc-text">7.1 在配置文件中区分 mode</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-2-运行时的环境变量"><span class="toc-text">7.2 运行时的环境变量</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-3-常见的环境差异配置"><span class="toc-text">7.3 常见的环境差异配置</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-4-拆分配置"><span class="toc-text">7.4 拆分配置</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-5-完整代码"><span class="toc-text">7.5 完整代码</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#八、模块热替换提高开发效率"><span class="toc-text">八、模块热替换提高开发效率</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#8-1-配置使用-HMR"><span class="toc-text">8.1 配置使用 HMR</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-2-module-hot-常见的-API"><span class="toc-text">8.2 module.hot 常见的 API</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#九、图片加载优化"><span class="toc-text">九、图片加载优化</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#9-1-CSS-Sprites"><span class="toc-text">9.1 CSS Sprites</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-2-图片压缩"><span class="toc-text">9.2 图片压缩</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-3-使用-DataURL"><span class="toc-text">9.3 使用 DataURL</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-4-代码压缩"><span class="toc-text">9.4 代码压缩</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#十、分离代码文件"><span class="toc-text">十、分离代码文件</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#10-1-webpack-4-x-的-optimization"><span class="toc-text">10.1 webpack 4.x 的 optimization</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#10-2-webpack-3-x-的-CommonsChunkPlugin"><span class="toc-text">10.2 webpack 3.x 的 CommonsChunkPlugin</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#十一、进一步控制JS大小"><span class="toc-text">十一、进一步控制JS大小</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#11-1-按需加载模块"><span class="toc-text">11.1 按需加载模块</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#11-2-以上完整示例代码"><span class="toc-text">11.2 以上完整示例代码</span></a></li></ol></li></ol>
  </div>
  




<div class="content content-post CENTER">
   <!-- canvas 彩带 -->
<canvas id="evanyou" width="1302" height="678" style="position: fixed;width: 100%;height: 100%;top: 0;left:0;z-index:-1;"></canvas>

<article id="post-webpack-custom-work-flow" class="article article-type-post" itemprop="blogPost">
  <header class="article-header" style="position:relative;">
    <h1 class="post-title">webpack4定制前端开发环境</h1>

    <div class="article-meta">
      <span>
        <i class="icon-calendar"></i>
        <span>2018.09.04</span>
      </span>

      
        <span class="article-author">
          <i class="icon-user"></i>
          <span>Poetry</span>
        </span>
      

      
  <span class="article-category">
    <i class="icon-list"></i>
    <a class="article-category-link" href="/categories/Build/">Build</a>
  </span>



      

      
      <i class="fa fa-eye"></i> 
        <span id="busuanzi_container_page_pv">
           &nbsp热度 <span id="busuanzi_value_page_pv">
           <i class="fa fa-spinner fa-spin"></i></span>℃
        </span>
      
      
       
          <span class="post-count">
            <i class="fa fa-file-word-o"></i>&nbsp
            <span>字数统计 11.7k字</span>
          </span>

          <span class="post-count">
            <i class="fa fa-columns"></i>&nbsp
            <span>阅读时长 48分</span>
          </span>
      
      
    </div>

    <i class="iconfont" id="toc-eye" style="display:inline-block;color:#b36619;position:absolute;top:0;right:0;cursor:pointer;
    font-size: 24px;">&#xe61c;</i>

  </header>

  <div class="article-content">
    
      <div id="container">
        <blockquote>
<p>来源掘金小册笔记</p>
</blockquote>
<h1 id="一、webpack概念和基础使用"><a href="#一、webpack概念和基础使用" class="headerlink" title="一、webpack概念和基础使用"></a>一、webpack概念和基础使用</h1><h2 id="1-1-安装和使用"><a href="#1-1-安装和使用" class="headerlink" title="1.1 安装和使用"></a>1.1 安装和使用</h2><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">npm install webpack webpack-cli -g </span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 或者</span></span><br><span class="line">yarn global add webpack webpack-cli</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 然后就可以全局执行命令了</span></span><br><span class="line">webpack --help</span><br></pre></td></tr></table></figure>
<h2 id="1-2-webpack-的基本概念"><a href="#1-2-webpack-的基本概念" class="headerlink" title="1.2 webpack 的基本概念"></a>1.2 webpack 的基本概念</h2><blockquote>
<p>webpack 本质上是一个打包工具，它会根据代码的内容解析模块依赖，帮助我们把多个模块的代码打包</p>
</blockquote>
<p><img src="https://poetries1.gitee.io/img-repo/2019/10/638.png" alt></p>
<blockquote>
<p>webpack 会把我们项目中使用到的多个代码模块（可以是不同文件类型），打包构建成项目运行仅需要的几个静态文件</p>
</blockquote>
<p><strong>入口</strong></p>
<blockquote>
<p>入口可以使用 <code>entry</code>字段来进行配置，<code>webpack</code> 支持配置多个入口来进行构建</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: <span class="string">'./src/index.js'</span> </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 上述配置等同于</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: &#123;</span><br><span class="line">    main: <span class="string">'./src/index.js'</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者配置多个入口</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: &#123;</span><br><span class="line">    foo: <span class="string">'./src/page-foo.js'</span>,</span><br><span class="line">    bar: <span class="string">'./src/page-bar.js'</span>, </span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用数组来对多个文件进行打包</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: &#123;</span><br><span class="line">    main: [</span><br><span class="line">      <span class="string">'./src/foo.js'</span>,</span><br><span class="line">      <span class="string">'./src/bar.js'</span></span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<p><strong>loader</strong></p>
<blockquote>
<p>可以把 <code>loader</code>理解为是一个转换器，负责把某种文件格式的内容转换成 webpack 可以支持打包的模块</p>
</blockquote>
<ul>
<li>当我们需要使用不同的 <code>loader</code> 来解析处理不同类型的文件时，我们可以在 <code>module.rules</code> 字段下来配置相关的规则，例如使用 <code>Babel</code> 来处理 <code>.js</code> 文件</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>: &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  rules: [</span><br><span class="line">    &#123;</span><br><span class="line">      test: <span class="regexp">/\.jsx?/</span>, <span class="comment">// 匹配文件路径的正则表达式，通常我们都是匹配文件类型后缀</span></span><br><span class="line">      include: [</span><br><span class="line">        path.resolve(__dirname, <span class="string">'src'</span>) <span class="comment">// 指定哪些路径下的文件需要经过 loader 处理</span></span><br><span class="line">      ],</span><br><span class="line">      use: <span class="string">'babel-loader'</span>, <span class="comment">// 指定使用的 loader</span></span><br><span class="line">    &#125;,</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<p><strong>plugin</strong></p>
<blockquote>
<p>模块代码转换的工作由 <code>loader</code> 来处理，除此之外的其他任何工作都可以交由 <code>plugin</code> 来完成。通过添加我们需要的 <code>plugin</code>，可以满足更多构建中特殊的需求。例如，要使用压缩 <code>JS</code>代码的 <code>uglifyjs-webpack-plugin</code> 插件，只需在配置中通过 <code>plugins</code>字段添加新的 <code>plugin</code>即可…</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> UglifyPlugin = <span class="built_in">require</span>(<span class="string">'uglifyjs-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> UglifyPlugin()</span><br><span class="line">  ],</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>plugin</code> 理论上可以干涉 <code>webpack</code> 整个构建流程，可以在流程的每一个步骤中定制自己的构建需求</p>
</blockquote>
<p><strong>输出</strong></p>
<blockquote>
<p>构建结果的文件名、路径等都是可以配置的，使用 <code>output</code>字段</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  output: &#123;</span><br><span class="line">    path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">    filename: <span class="string">'bundle.js'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者多个入口生成不同文件</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: &#123;</span><br><span class="line">    foo: <span class="string">'./src/foo.js'</span>,</span><br><span class="line">    bar: <span class="string">'./src/bar.js'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  output: &#123;</span><br><span class="line">    filename: <span class="string">'[name].js'</span>,</span><br><span class="line">    path: __dirname + <span class="string">'/dist'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 路径中使用 hash，每次构建时会有一个不同 hash 值，避免发布新版本时线上使用浏览器缓存</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  output: &#123;</span><br><span class="line">    filename: <span class="string">'[name].js'</span>,</span><br><span class="line">    path: __dirname + <span class="string">'/dist/[hash]'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>我们一开始直接使用 <code>webpack</code> 构建时，默认创建的输出内容就是 <code>./dist/main.js</code></p>
</blockquote>
<p><strong>一个简单的 webpack 配置</strong></p>
<blockquote>
<p>我们把上述涉及的几部分配置内容合到一起，就可以创建一个简单的 <code>webpack</code> 配置了，<code>webpack</code> 运行时默认读取项目下的 <code>webpack.config.js</code> 文件作为配置。所以我们在项目中创建一个 <code>webpack.config.js</code> 文件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">const</span> UglifyPlugin = <span class="built_in">require</span>(<span class="string">'uglifyjs-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: <span class="string">'./src/index.js'</span>,</span><br><span class="line"></span><br><span class="line">  output: &#123;</span><br><span class="line">    path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">    filename: <span class="string">'bundle.js'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.jsx?/</span>,</span><br><span class="line">        include: [</span><br><span class="line">          path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">        ],</span><br><span class="line">        use: <span class="string">'babel-loader'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 代码模块路径解析的配置</span></span><br><span class="line">  resolve: &#123;</span><br><span class="line">    modules: [</span><br><span class="line">      <span class="string">"node_modules"</span>,</span><br><span class="line">      path.resolve(__dirname, <span class="string">'src'</span>)</span><br><span class="line">    ],</span><br><span class="line"></span><br><span class="line">    extensions: [<span class="string">".wasm"</span>, <span class="string">".mjs"</span>, <span class="string">".js"</span>, <span class="string">".json"</span>, <span class="string">".jsx"</span>],</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> UglifyPlugin(), </span><br><span class="line">    <span class="comment">// 使用 uglifyjs-webpack-plugin 来压缩 JS 代码</span></span><br><span class="line">    <span class="comment">// 如果你留意了我们一开始直接使用 webpack 构建的结果，你会发现默认已经使用了 JS 代码压缩的插件</span></span><br><span class="line">    <span class="comment">// 这其实也是我们命令中的 --mode production 的效果，后续的小节会介绍 webpack 的 mode 参数</span></span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h1 id="二、搭建基础的前端开发环境"><a href="#二、搭建基础的前端开发环境" class="headerlink" title="二、搭建基础的前端开发环境"></a>二、搭建基础的前端开发环境</h1><h2 id="2-1-关联-HTML"><a href="#2-1-关联-HTML" class="headerlink" title="2.1 关联 HTML"></a>2.1 关联 HTML</h2><blockquote>
<p><code>webpack</code> 默认从作为入口的 <code>.js</code> 文件进行构建（更多是基于 <code>SPA</code> 去考虑），但通常一个前端项目都是从一个页面（即 HTML）出发的，最简单的方法是，创建一个 HTML 文件，使用 <code>script</code> 标签直接引用构建好的 JS 文件，如…</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">&lt;script src=&quot;./dist/bundle.js&quot;&gt;&lt;/script&gt;</span><br></pre></td></tr></table></figure>
<ul>
<li>但是，如果我们的文件名或者路径会变化，例如使用 <code>[hash]</code> 来进行命名，那么最好是将 <code>HTML</code> 引用路径和我们的构建结果关联起来，这个时候我们可以使用 <code>html-webpack-plugin</code></li>
<li><code>html-webpack-plugin</code> 是一个独立的 <code>node package</code>，所以在使用之前我们需要先安装它，把它安装到项目的开发依赖中</li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">npm install html-webpack-plugin -D</span><br></pre></td></tr></table></figure>
<blockquote>
<p>然后在 <code>webpack</code>配置中，将 <code>html-webpack-plugin</code> 添加到 <code>plugins</code> 列表中</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> HtmlWebpackPlugin(),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这样配置好之后，构建时 <code>html-webpack-plugin</code> 会为我们创建一个 <code>HTML</code> 文件，其中会引用构建出来的 JS 文件。实际项目中，默认创建的 <code>HTML</code> 文件并没有什么用，我们需要自己来写 <code>HTML</code> 文件，可以通过 <code>html-webpack-plugin</code> 的配置，传递一个写好的 HTML 模板…</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">      filename: <span class="string">'index.html'</span>, <span class="comment">// 配置输出文件名和路径</span></span><br><span class="line">      template: <span class="string">'assets/index.html'</span>, <span class="comment">// 配置文件模板</span></span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这样，通过 <code>html-webpack-plugin</code> 就可以将我们的页面和构建 <code>JS</code> 关联起来，回归日常，从页面开始开发。如果需要添加多个页面关联，那么实例化多个 <code>html-webpack-plugin</code>， 并将它们都放到 <code>plugins</code> 字段数组中就可以了…</p>
</blockquote>
<h2 id="2-2-构建-CSS"><a href="#2-2-构建-CSS" class="headerlink" title="2.2 构建 CSS"></a>2.2 构建 CSS</h2><blockquote>
<p>我们编写 <code>CSS</code>，并且希望使用 <code>webpack</code> 来进行构建，为此，需要在配置中引入 <code>loader</code> 来解析和处理 <code>CSS</code> 文件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      <span class="comment">// ...</span></span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.css/</span>,</span><br><span class="line">        include: [</span><br><span class="line">          path.resolve(__dirname, <span class="string">'src'</span>),</span><br><span class="line">        ],</span><br><span class="line">        use: [</span><br><span class="line">          <span class="string">'style-loader'</span>,</span><br><span class="line">          <span class="string">'css-loader'</span>,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<ul>
<li><code>css-loader</code> 负责解析 <code>CSS</code> 代码，主要是为了处理 <code>CSS</code> 中的依赖，例如 <code>@import</code> 和 <code>url()</code> 等引用外部文件的声明；</li>
<li><code>style-loader</code> 会将 <code>css-loader</code> 解析的结果转变成 <code>JS</code>代码，运行时动态插入 <code>style</code> 标签来让 <code>CSS</code> 代码生效…</li>
</ul>
<blockquote>
<p>经由上述两个 <code>loader</code> 的处理后，CSS 代码会转变为 JS，和 <code>index.js</code>一起打包了。如果需要单独把 CSS 文件分离出来，我们需要使用 <code>extract-text-webpack-plugin</code> 插件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> ExtractTextPlugin = <span class="built_in">require</span>(<span class="string">'extract-text-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.css$/</span>,</span><br><span class="line">        <span class="comment">// 因为这个插件需要干涉模块转换的内容，所以需要使用它对应的 loader</span></span><br><span class="line">        use: ExtractTextPlugin.extract(&#123; </span><br><span class="line">          fallback: <span class="string">'style-loader'</span>,</span><br><span class="line">          use: <span class="string">'css-loader'</span>,</span><br><span class="line">        &#125;), </span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="comment">// 引入插件，配置文件名，这里同样可以使用 [hash]</span></span><br><span class="line">    <span class="keyword">new</span> ExtractTextPlugin(<span class="string">'index.css'</span>),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h2 id="2-3-CSS-预处理器"><a href="#2-3-CSS-预处理器" class="headerlink" title="2.3 CSS 预处理器"></a>2.3 CSS 预处理器</h2><blockquote>
<p>在上述使用 CSS 的基础上，通常我们会使用 <code>Less/Sass</code> 等 CSS 预处理器，webpack 可以通过添加对应的 <code>loader</code> 来支持，以使用 <code>Less</code> 为例，我们可以在官方文档中找到对应的 <code>loader</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.less$/</span>,</span><br><span class="line">        <span class="comment">// 因为这个插件需要干涉模块转换的内容，所以需要使用它对应的 loader</span></span><br><span class="line">        use: ExtractTextPlugin.extract(&#123; </span><br><span class="line">          fallback: <span class="string">'style-loader'</span>,</span><br><span class="line">          use: [</span><br><span class="line">            <span class="string">'css-loader'</span>, </span><br><span class="line">            <span class="string">'less-loader'</span>,</span><br><span class="line">          ],</span><br><span class="line">        &#125;), </span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h2 id="2-4-处理图片文件"><a href="#2-4-处理图片文件" class="headerlink" title="2.4 处理图片文件"></a>2.4 处理图片文件</h2><blockquote>
<p>在前端项目的样式中总会使用到图片，虽然我们已经提到 <code>css-loader</code> 会解析样式中用 <code>url()</code> 引用的文件路径，但是图片对应的 <code>jpg/png/gif</code> 等文件格式，<code>webpack</code> 处理不了。是的，我们只要添加一个处理图片的 <code>loader</code> 配置就可以了，现有的 <code>file-loader</code> 就是个不错的选择…</p>
</blockquote>
<ul>
<li><code>file-loader</code> 可以用于处理很多类型的文件，它的主要作用是直接输出文件，把构建后的文件路径返回。配置很简单，在 <code>rules</code>中添加一个字段，增加图片类型文件的解析配置</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.(png|jpg|gif)$/</span>,</span><br><span class="line">        use: [</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'file-loader'</span>,</span><br><span class="line">            options: &#123;&#125;,</span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h2 id="2-5-使用-Babel"><a href="#2-5-使用-Babel" class="headerlink" title="2.5 使用 Babel"></a>2.5 使用 Babel</h2><blockquote>
<p><code>Babel</code> 是一个让我们能够使用 <code>ES</code> 新特性的 <code>JS</code> 编译工具，我们可以在 <code>webpack</code> 中配置 Babel，以便使用 <code>ES6</code>、<code>ES7</code> 标准来编写 <code>JS</code>代码</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.jsx?/</span>, <span class="comment">// 支持 js 和 jsx</span></span><br><span class="line">        include: [</span><br><span class="line">          path.resolve(__dirname, <span class="string">'src'</span>), <span class="comment">// src 目录下的才需要经过 babel-loader 处理</span></span><br><span class="line">        ],</span><br><span class="line">        loader: <span class="string">'babel-loader'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h2 id="2-6-启动静态服务"><a href="#2-6-启动静态服务" class="headerlink" title="2.6 启动静态服务"></a>2.6 启动静态服务</h2><blockquote>
<p>至此，我们完成了处理多种文件类型的 webpack 配置。我们可以使用 <code>webpack-dev-server</code> 在本地开启一个简单的静态服务来进行开发</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="string">"scripts"</span>: &#123;</span><br><span class="line">  <span class="string">"build"</span>: <span class="string">"webpack --mode production"</span>,</span><br><span class="line">  <span class="string">"start"</span>: <span class="string">"webpack-dev-server --mode development"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>尝试着运行 <code>npm start</code> 或者 <code>yarn start</code>，然后就可以访问<code>http://localhost:8080/</code> 来查看你的页面了。默认是访问 <code>index.html</code>，如果是其他页面要注意访问的 URL 是否正确</p>
</blockquote>
<h2 id="2-7-完整示例代码"><a href="#2-7-完整示例代码" class="headerlink" title="2.7 完整示例代码"></a>2.7 完整示例代码</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">const</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>)</span><br><span class="line"><span class="keyword">const</span> ExtractTextPlugin = <span class="built_in">require</span>(<span class="string">'extract-text-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: <span class="string">'./src/index.js'</span>,</span><br><span class="line"></span><br><span class="line">  output: &#123;</span><br><span class="line">    path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">    filename: <span class="string">'[name].js'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.jsx?/</span>,</span><br><span class="line">        include: [</span><br><span class="line">          path.resolve(__dirname, <span class="string">'src'</span>),</span><br><span class="line">        ],</span><br><span class="line">        use: <span class="string">'babel-loader'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.less$/</span>,</span><br><span class="line">        <span class="comment">// 因为这个插件需要干涉模块转换的内容，所以需要使用它对应的 loader</span></span><br><span class="line">        use: ExtractTextPlugin.extract(&#123; </span><br><span class="line">          fallback: <span class="string">'style-loader'</span>,</span><br><span class="line">          use: [</span><br><span class="line">            <span class="string">'css-loader'</span>, </span><br><span class="line">            <span class="string">'less-loader'</span>,</span><br><span class="line">          ],</span><br><span class="line">        &#125;), </span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.(png|jpg|gif)$/</span>,</span><br><span class="line">        use: [</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'file-loader'</span></span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 代码模块路径解析的配置</span></span><br><span class="line">  resolve: &#123;</span><br><span class="line">    modules: [</span><br><span class="line">      <span class="string">"node_modules"</span>,</span><br><span class="line">      path.resolve(__dirname, <span class="string">'src'</span>),</span><br><span class="line">    ],</span><br><span class="line"></span><br><span class="line">    extensions: [<span class="string">".wasm"</span>, <span class="string">".mjs"</span>, <span class="string">".js"</span>, <span class="string">".json"</span>, <span class="string">".jsx"</span>],</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">      filename: <span class="string">'index.html'</span>, <span class="comment">// 配置输出文件名和路径</span></span><br><span class="line">      template: <span class="string">'src/index.html'</span>, <span class="comment">// 配置文件模板</span></span><br><span class="line">    &#125;),</span><br><span class="line">    <span class="keyword">new</span> ExtractTextPlugin(<span class="string">'[name].css'</span>),</span><br><span class="line">  ],</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="三、webpack如何解析代码模块路径"><a href="#三、webpack如何解析代码模块路径" class="headerlink" title="三、webpack如何解析代码模块路径"></a>三、webpack如何解析代码模块路径</h1><blockquote>
<p>webpack 中有一个很关键的模块 <code>enhanced-resolve</code> 就是处理依赖模块路径的解析的，这个模块可以说是 Node.js 那一套模块路径解析的增强版本，有很多可以自定义的解析配置</p>
</blockquote>
<ul>
<li>在 webpack 配置中，和模块路径解析相关的配置都在 <code>resolve</code>字段下</li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">module.exports = &#123;</span><br><span class="line">  resolve: &#123;</span><br><span class="line">    // ...</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="3-1-常用的一些配置"><a href="#3-1-常用的一些配置" class="headerlink" title="3.1 常用的一些配置"></a>3.1 常用的一些配置</h2><p><strong>resolve.alias</strong></p>
<blockquote>
<p>假设我们有个 <code>utils</code> 模块极其常用，经常编写相对路径很麻烦，希望可以直接 <code>import &#39;utils&#39;</code> 来引用，那么我们可以配置某个模块的别名，如</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">alias: &#123;</span><br><span class="line">  utils: path.resolve(__dirname, <span class="string">'src/utils'</span>) <span class="comment">// 这里使用 path.resolve 和 __dirname 来获取绝对路径</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上述的配置是模糊匹配，意味着只要模块路径中携带了 utils 就可以被替换掉，如：</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">import &apos;utils/query.js&apos; // 等同于 import &apos;[项目绝对路径]/src/utils/query.js&apos;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果需要进行精确匹配可以使用：</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">alias: &#123;</span><br><span class="line">  utils$: path.resolve(__dirname, <span class="string">'src/utils'</span>) <span class="comment">// 只会匹配 import 'utils'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>resolve.extensions</strong></p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">extensions: [<span class="string">'.wasm'</span>, <span class="string">'.mjs'</span>, <span class="string">'.js'</span>, <span class="string">'.json'</span>, <span class="string">'.jsx'</span>],</span><br><span class="line"><span class="comment">// 这里的顺序代表匹配后缀的优先级，例如对于 index.js 和 index.jsx，会优先选择 index.js</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>这个配置的作用是和文件后缀名有关的,这个配置可以定义在进行模块路径解析时，webpack 会尝试帮你补全那些后缀名来进行查找</p>
</blockquote>
<h1 id="四、配置loader"><a href="#四、配置loader" class="headerlink" title="四、配置loader"></a>四、配置loader</h1><h2 id="4-1-loader-匹配规则"><a href="#4-1-loader-匹配规则" class="headerlink" title="4.1 loader 匹配规则"></a>4.1 loader 匹配规则</h2><blockquote>
<p>当我们需要配置 <code>loader</code> 时，都是在 <code>module.rules</code> 中添加新的配置项，在该字段中，每一项被视为一条匹配使用 <code>loader</code>的规则</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [ </span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.jsx?/</span>, <span class="comment">// 条件</span></span><br><span class="line">        include: [ </span><br><span class="line">          path.resolve(__dirname, <span class="string">'src'</span>),</span><br><span class="line">        ], <span class="comment">// 条件</span></span><br><span class="line">        use: <span class="string">'babel-loader'</span>, <span class="comment">// 规则应用结果</span></span><br><span class="line">      &#125;, <span class="comment">// 一个 object 即一条规则</span></span><br><span class="line">      <span class="comment">// ...</span></span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>loader</code> 的匹配规则中有两个最关键的因素：一个是匹配条件，一个是匹配规则后的应用</p>
</blockquote>
<h2 id="4-2-规则条件配置"><a href="#4-2-规则条件配置" class="headerlink" title="4.2 规则条件配置"></a>4.2 规则条件配置</h2><blockquote>
<p>大多数情况下，配置 <code>loader</code> 的匹配条件时，只要使用 <code>test</code> 字段就好了，很多时候都只需要匹配文件后缀名来决定使用什么 <code>loader</code>，但也不排除在某些特殊场景下，我们需要配置比较复杂的匹配条件。webpack 的规则提供了多种配置形式…</p>
</blockquote>
<ul>
<li><code>{ test: ... }</code> 匹配特定条件</li>
<li><code>{ include: ... }</code> 匹配特定路径</li>
<li><code>{ exclude: ... }</code>排除特定路径</li>
<li><code>{ and: [...] }</code>必须匹配数组中所有条件</li>
<li><code>{ or: [...] }</code>匹配数组中任意一个条件</li>
<li><code>{ not: [...] }</code> 排除匹配数组中所有条件…</li>
</ul>
<blockquote>
<p>上述的所谓条件的值可以是：</p>
</blockquote>
<ul>
<li>字符串：必须以提供的字符串开始，所以是字符串的话，这里我们需要提供绝对路径</li>
<li>正则表达式：调用正则的 <code>test</code> 方法来判断匹配</li>
<li>函数：<code>(path) =&gt; boolean</code>，返回 <code>true</code> 表示匹配</li>
<li>数组：至少包含一个条件的数组</li>
<li>对象：匹配所有属性值的条件…</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">rules: [</span><br><span class="line">  &#123;</span><br><span class="line">    test: <span class="regexp">/\.jsx?/</span>, <span class="comment">// 正则</span></span><br><span class="line">    include: [</span><br><span class="line">      path.resolve(__dirname, <span class="string">'src'</span>), <span class="comment">// 字符串，注意是绝对路径</span></span><br><span class="line">    ], <span class="comment">// 数组</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;,</span><br><span class="line">  &#123;</span><br><span class="line">    test: &#123;</span><br><span class="line">      js: <span class="regexp">/\.js/</span>,</span><br><span class="line">      jsx: <span class="regexp">/\.jsx/</span>,</span><br><span class="line">    &#125;, <span class="comment">// 对象，不建议使用</span></span><br><span class="line">    not: [</span><br><span class="line">      (value) =&gt; &#123; <span class="comment">/* ... */</span> <span class="keyword">return</span> <span class="literal">true</span>; &#125;, <span class="comment">// 函数，通常需要高度自定义时才会使用</span></span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">],...</span><br></pre></td></tr></table></figure>
<h2 id="4-3-使用-loader-配置"><a href="#4-3-使用-loader-配置" class="headerlink" title="4.3 使用 loader 配置"></a>4.3 使用 loader 配置</h2><blockquote>
<p><code>module.rules</code> 的匹配规则最重要的还是用于配置 <code>loader</code>，我们可以使用 <code>use</code> 字段</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">rules: [</span><br><span class="line">  &#123;</span><br><span class="line">    test: <span class="regexp">/\.less/</span>,</span><br><span class="line">    use: [</span><br><span class="line">      <span class="string">'style-loader'</span>, <span class="comment">// 直接使用字符串表示 loader</span></span><br><span class="line">      &#123;</span><br><span class="line">        loader: <span class="string">'css-loader'</span>,</span><br><span class="line">        options: &#123;</span><br><span class="line">          importLoaders: <span class="number">1</span></span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;, <span class="comment">// 用对象表示 loader，可以传递 loader 配置等</span></span><br><span class="line">      &#123;</span><br><span class="line">        loader: <span class="string">'less-loader'</span>,</span><br><span class="line">        options: &#123;</span><br><span class="line">          noIeCompat: <span class="literal">true</span></span><br><span class="line">        &#125;, <span class="comment">// 传递 loader 配置</span></span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">],...</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>use</code>字段可以是一个数组，也可以是一个字符串或者表示 <code>loader</code> 的对象。如果只需要一个 <code>loader</code>，也可以这样：<code>use: { loader: &#39;babel-loader&#39;</code>, <code>options: { ... } }</code></p>
</blockquote>
<h2 id="4-4-loader-应用顺序"><a href="#4-4-loader-应用顺序" class="headerlink" title="4.4 loader 应用顺序"></a>4.4 loader 应用顺序</h2><ul>
<li>对于上面的 <code>less</code> 规则配置，一个 <code>style.less</code> 文件会途径 <code>less-loader</code>、<code>css-loader</code>、<code>style-loader</code> 处理，成为一个可以打包的模块。</li>
<li><code>loader</code> 的应用顺序在配置多个 <code>loader</code> 一起工作时很重要，通常会使用在 CSS 配置上，除了 <code>style-loader</code> 和 <code>css-loader</code>，你可能还要配置 <code>less-loader</code>然后再加个 <code>postcss</code> 的 <code>autoprefixer</code> 等。</li>
<li>上述从后到前的顺序是在同一个 <code>rule</code> 中进行的，那如果多个 <code>rule</code> 匹配了同一个模块文件，<code>loader</code> 的应用顺序又是怎样的呢？看一份这样的配置…</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">rules: [</span><br><span class="line">  &#123;</span><br><span class="line">    test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">    exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">    loader: <span class="string">"eslint-loader"</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  &#123;</span><br><span class="line">    test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">    exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">    loader: <span class="string">"babel-loader"</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">],...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这样无法法保证 <code>eslint-loader</code> 在 <code>babel-loader</code> 应用前执行。<code>webpack</code>在 r<code>ules</code> 中提供了一个 <code>enforce</code> 的字段来配置当前 <code>rule</code> 的 <code>loader</code> 类型，没配置的话是普通类型，我们可以配置 <code>pre</code>或<code>post</code>，分别对应前置类型或后置类型的 <code>loader</code>…</p>
</blockquote>
<ul>
<li>所有的 <code>loader</code> <strong>按照前置</strong> -&gt; <strong>行</strong>内 -&gt; <strong>普通</strong> -&gt; <strong>后置</strong>的顺序执行。所以当我们要确保 <code>eslint-loader</code> 在 <code>babel-loader</code> 之前执行时，可以如下添加 <code>enforce</code> 配置</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">rules: [</span><br><span class="line">  &#123;</span><br><span class="line">    enforce: <span class="string">'pre'</span>, <span class="comment">// 指定为前置类型</span></span><br><span class="line">    test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">    exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">    loader: <span class="string">"eslint-loader"</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">]...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>当项目文件类型和应用的 <code>loader</code> 不是特别复杂的时候，通常建议把要应用的同一类型 <code>loader</code> 都写在同一个匹配规则中，这样更好维护和控制</p>
</blockquote>
<h2 id="4-5-完整代码"><a href="#4-5-完整代码" class="headerlink" title="4.5 完整代码"></a>4.5 完整代码</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">'webpack'</span>)</span><br><span class="line"><span class="keyword">const</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>)</span><br><span class="line"><span class="keyword">const</span> ExtractTextPlugin = <span class="built_in">require</span>(<span class="string">'extract-text-webpack-plugin'</span>)</span><br><span class="line"><span class="keyword">const</span> CopyWebpackPlugin = <span class="built_in">require</span>(<span class="string">'copy-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: <span class="string">'./src/index'</span>,</span><br><span class="line"></span><br><span class="line">  output: &#123;</span><br><span class="line">    path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">    filename: <span class="string">'[name].js'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        enforce: <span class="string">'pre'</span>, <span class="comment">// 指定为前置类型</span></span><br><span class="line">        test: <span class="regexp">/\.jsx?$/</span>,</span><br><span class="line">        exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">        loader: <span class="string">"eslint-loader"</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.jsx?$/</span>,</span><br><span class="line">        include: [</span><br><span class="line">          path.resolve(__dirname, <span class="string">'src'</span>),</span><br><span class="line">        ],</span><br><span class="line">        use: <span class="string">'babel-loader'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.css$/</span>,</span><br><span class="line">        use: ExtractTextPlugin.extract(&#123;</span><br><span class="line">          fallback: <span class="string">'style-loader'</span>,</span><br><span class="line">          use: [</span><br><span class="line">            <span class="string">'css-loader'</span>,</span><br><span class="line">          ],</span><br><span class="line">        &#125;),</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.less$/</span>,</span><br><span class="line">        use: ExtractTextPlugin.extract(&#123;</span><br><span class="line">          fallback: <span class="string">'style-loader'</span>,</span><br><span class="line">          use: [</span><br><span class="line">            <span class="string">'css-loader'</span>,</span><br><span class="line">            <span class="string">'less-loader'</span>,</span><br><span class="line">          ],</span><br><span class="line">        &#125;),</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.(png|jpg|gif)$/</span>,</span><br><span class="line">        use: [</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'file-loader'</span></span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  resolve: &#123;</span><br><span class="line">    alias: &#123;</span><br><span class="line">      utils: path.resolve(__dirname, <span class="string">'src/utils'</span>), <span class="comment">// 这里使用 path.resolve 和 __dirname 来获取绝对路径</span></span><br><span class="line">      log$: path.resolve(__dirname, <span class="string">'src/utils/log.js'</span>) <span class="comment">// 只匹配 log</span></span><br><span class="line">    &#125;,</span><br><span class="line">    extensions: [<span class="string">'.js'</span>, <span class="string">'.json'</span>, <span class="string">'.jsx'</span>, <span class="string">'.css'</span>, <span class="string">'.less'</span>],</span><br><span class="line">    modules: [</span><br><span class="line">      path.resolve(__dirname, <span class="string">'node_modules'</span>), <span class="comment">// 指定当前目录下的 node_modules 优先查找</span></span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">      filename: <span class="string">'index.html'</span>, <span class="comment">// 配置输出文件名和路径</span></span><br><span class="line">      template: <span class="string">'src/index.html'</span>, <span class="comment">// 配置文件模板</span></span><br><span class="line">    &#125;),</span><br><span class="line">    <span class="keyword">new</span> ExtractTextPlugin(<span class="string">'[name].css'</span>),</span><br><span class="line">    <span class="keyword">new</span> webpack.DefinePlugin(&#123;</span><br><span class="line">      TWO: <span class="string">'1+1'</span>,</span><br><span class="line">      CONSTANTS: &#123;</span><br><span class="line">        APP_VERSION: <span class="built_in">JSON</span>.stringify(<span class="string">'1.1.2'</span>), <span class="comment">// const CONSTANTS = &#123; APP_VERSION: '1.1.2' &#125;</span></span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;),</span><br><span class="line">    <span class="keyword">new</span> CopyWebpackPlugin([</span><br><span class="line">      &#123; <span class="attr">from</span>: <span class="string">'src/assets/favicon.ico'</span>, <span class="attr">to</span>: <span class="string">'favicon.ico'</span>, &#125;, <span class="comment">// 顾名思义，from 配置来源，to 配置目标路径</span></span><br><span class="line">    ]),</span><br><span class="line">    <span class="keyword">new</span> webpack.ProvidePlugin(&#123;</span><br><span class="line">      _: <span class="string">'lodash'</span>,</span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line"></span><br><span class="line">  devServer: &#123;</span><br><span class="line">    port: <span class="string">'1234'</span>,</span><br><span class="line">    before(app)&#123;</span><br><span class="line">      app.get(<span class="string">'/api/test.json'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>) </span>&#123; <span class="comment">// 当访问 /some/path 路径时，返回自定义的 json 数据</span></span><br><span class="line">        res.json(&#123; <span class="attr">code</span>: <span class="number">200</span>, <span class="attr">message</span>: <span class="string">'hello world'</span> &#125;)</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="五、使用plugin"><a href="#五、使用plugin" class="headerlink" title="五、使用plugin"></a>五、使用plugin</h1><blockquote>
<p>更多的插件可以在这里查找：<a href="https://github.com/webpack-contrib/awesome-webpack#webpack-plugins" target="_blank" rel="noopener">plugins in awesome-webpack</a></p>
</blockquote>
<h2 id="5-1-DefinePlugin"><a href="#5-1-DefinePlugin" class="headerlink" title="5.1 DefinePlugin"></a>5.1 DefinePlugin</h2><blockquote>
<p><code>DefinePlugin</code> 是 <code>webpack</code> 内置的插件，可以使用 <code>webpack.DefinePlugin</code> 直接获取</p>
</blockquote>
<ul>
<li>这个插件用于创建一些在编译时可以配置的全局常量，这些常量的值我们可以在 <code>webpack</code> 的配置中去指定，例如</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> webpack.DefinePlugin(&#123;</span><br><span class="line">      PRODUCTION: <span class="built_in">JSON</span>.stringify(<span class="literal">true</span>), <span class="comment">// const PRODUCTION = true</span></span><br><span class="line">      VERSION: <span class="built_in">JSON</span>.stringify(<span class="string">'5fa3b9'</span>), <span class="comment">// const VERSION = '5fa3b9'</span></span><br><span class="line">      BROWSER_SUPPORTS_HTML5: <span class="literal">true</span>, <span class="comment">// const BROWSER_SUPPORTS_HTML5 = 'true'</span></span><br><span class="line">      TWO: <span class="string">'1+1'</span>, <span class="comment">// const TWO = 1 + 1,</span></span><br><span class="line">      CONSTANTS: &#123;</span><br><span class="line">        APP_VERSION: <span class="built_in">JSON</span>.stringify(<span class="string">'1.1.2'</span>) <span class="comment">// const CONSTANTS = &#123; APP_VERSION: '1.1.2' &#125;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>有了上面的配置，就可以在应用代码文件中，访问配置好的变量了，如：</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="string">"Running App version "</span> + VERSION);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(!BROWSER_SUPPORTS_HTML5) <span class="built_in">require</span>(<span class="string">"html5shiv"</span>);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上面配置的注释已经简单说明了这些配置的效果，这里再简述一下整个配置规则。</p>
</blockquote>
<ul>
<li>如果配置的值是字符串，那么整个字符串会被当成代码片段来执行，其结果作为最终变量的值，如上面的 <code>&quot;1+1&quot;</code>，最后的结果是 <code>2</code></li>
<li>如果配置的值不是字符串，也不是一个对象字面量，那么该值会被转为一个字符串，如 <code>true</code>，最后的结果是 <code>&#39;true&#39;</code></li>
<li>如果配置的是一个对象字面量，那么该对象的所有 <code>key</code>会以同样的方式去定义</li>
<li>这样我们就可以理解为什么要使用 <code>JSON.stringify()</code> 了，因为 <code>JSON.stringify(true)</code> 的结果是 <code>&#39;true&#39;</code>，<code>JSON.stringify(&quot;5fa3b9&quot;)</code> 的结果是 <code>&quot;5fa3b9&quot;</code>。</li>
</ul>
<blockquote>
<p>社区中关于 <code>DefinePlugin</code> 使用得最多的方式是定义环境变量，例如 <code>PRODUCTION = true</code> 或者 <code>__DEV__ = true</code> 等。部分类库在开发环境时依赖这样的环境变量来给予开发者更多的开发调试反馈，例如 react 等。</p>
</blockquote>
<ul>
<li>建议使用 <code>process.env.NODE_ENV</code>: … 的方式来定义 <code>process.env.NODE_ENV</code>，而不是使用 <code>process: { env: { NODE_ENV: ... } }</code> 的方式，因为这样会覆盖掉 <code>process</code> 这个对象，可能会对其他代码造成影响…</li>
</ul>
<h2 id="5-2-copy-webpack-plugin"><a href="#5-2-copy-webpack-plugin" class="headerlink" title="5.2 copy-webpack-plugin"></a>5.2 copy-webpack-plugin</h2><blockquote>
<p>我们一般会把开发的所有源码和资源文件放在 <code>src/</code> 目录下，构建的时候产出一个 <code>build/</code> 目录，通常会直接拿 <code>build</code> 中的所有文件来发布。有些文件没经过 <code>webpack</code> 处理，但是我们希望它们也能出现在 <code>build</code> 目录下，这时就可以使用 <code>CopyWebpackPlugin</code> 来处理了…</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> CopyWebpackPlugin = <span class="built_in">require</span>(<span class="string">'copy-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> CopyWebpackPlugin([</span><br><span class="line">      &#123; <span class="attr">from</span>: <span class="string">'src/file.txt'</span>, <span class="attr">to</span>: <span class="string">'build/file.txt'</span>, &#125;, <span class="comment">// 顾名思义，from 配置来源，to 配置目标路径</span></span><br><span class="line">      &#123; <span class="attr">from</span>: <span class="string">'src/*.ico'</span>, <span class="attr">to</span>: <span class="string">'build/*.ico'</span> &#125;, <span class="comment">// 配置项可以使用 glob</span></span><br><span class="line">      <span class="comment">// 可以配置很多项复制规则</span></span><br><span class="line">    ]),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h2 id="5-3-extract-text-webpack-plugin"><a href="#5-3-extract-text-webpack-plugin" class="headerlink" title="5.3 extract-text-webpack-plugin"></a>5.3 extract-text-webpack-plugin</h2><blockquote>
<p>我们用它来把依赖的 <code>CSS</code> 分离出来成为单独的文件。这里再看一下使用 <code>extract-text-webpack-plugin</code> 的配置</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> ExtractTextPlugin = <span class="built_in">require</span>(<span class="string">'extract-text-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.css$/</span>,</span><br><span class="line">        <span class="comment">// 因为这个插件需要干涉模块转换的内容，所以需要使用它对应的 loader</span></span><br><span class="line">        use: ExtractTextPlugin.extract(&#123; </span><br><span class="line">          fallback: <span class="string">'style-loader'</span>,</span><br><span class="line">          use: <span class="string">'css-loader'</span>,</span><br><span class="line">        &#125;), </span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="comment">// 引入插件，配置文件名，这里同样可以使用 [hash]</span></span><br><span class="line">    <span class="keyword">new</span> ExtractTextPlugin(<span class="string">'index.css'</span>),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>在上述的配置中，我们使用了 <code>index.css</code> 作为单独分离出来的文件名，但有的时候构建入口不止一个，<code>extract-text-webpack-plugin</code> 会为每一个入口创建单独分离的文件，因此最好这样配置</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 这样确保在使用多个构建入口时，生成不同名称的文件</span></span><br><span class="line">plugins: [</span><br><span class="line">  <span class="keyword">new</span> ExtractTextPlugin(<span class="string">'[name].css'</span>),</span><br><span class="line">],</span><br></pre></td></tr></table></figure>
<h1 id="六、更好使用webpack-dev-server"><a href="#六、更好使用webpack-dev-server" class="headerlink" title="六、更好使用webpack-dev-server"></a>六、更好使用webpack-dev-server</h1><blockquote>
<p><code>webpack-dev-server</code> 是 <code>webpack</code> 官方提供的一个工具，可以基于当前的 <code>webpack</code> 构建配置快速启动一个静态服务。当 <code>mode</code> 为 <code>development</code> 时，会具备 <code>hot reload</code> 的功能，即当源码文件变化时，会即时更新当前页面，以便你看到最新的效果…</p>
</blockquote>
<h2 id="6-1-基础使用"><a href="#6-1-基础使用" class="headerlink" title="6.1 基础使用"></a>6.1 基础使用</h2><blockquote>
<p><code>webpack-dev-server</code> 是一个 <code>npm package</code>，安装后在已经有 <code>webpack</code> 配置文件的项目目录下直接启动就可以</p>
</blockquote>
<ul>
<li><code>webpack-dev-server</code> 默认使用 <code>8080</code> 端口</li>
</ul>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">npm install webpack-dev-server -g</span><br><span class="line">webpack-dev-server --mode development</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>package</code> 中的 <code>scripts</code> 配置：</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="string">"scripts"</span>: &#123;</span><br><span class="line">    <span class="string">"start"</span>: <span class="string">"webpack-dev-server --mode development"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="6-2-配置"><a href="#6-2-配置" class="headerlink" title="6.2 配置"></a>6.2 配置</h2><blockquote>
<p>在 webpack 的配置中，可以通过 <code>devServer</code> 字段来配置 <code>webpack-dev-server</code>，如端口设置、启动 <code>gzip</code> 压缩等，这里简单讲解几个常用的配置</p>
</blockquote>
<ul>
<li><code>public</code>字段用于指定静态服务的域名，默认是 <code>http://localhost:8080/</code> ，当你使用 <code>Nginx</code> 来做反向代理时，应该就需要使用该配置来指定 <code>Nginx</code> 配置使用的服务域名</li>
<li><code>port</code> 字段用于指定静态服务的端口，如上，默认是 <code>8080</code>，通常情况下都不需要改动</li>
<li><code>publicPath</code> 字段用于指定构建好的静态文件在浏览器中用什么路径去访问，默认是 <code>/</code>，例如，对于一个构建好的文件 <code>bundle.js</code>，完整的访问路径是 <code>http://localhost:8080/bundle.js</code>，如果你配置了 <code>publicPath: &#39;assets/&#39;</code>，那么上述 <code>bundle.js</code> 的完整访问路径就是 <code>http://localhost:8080/assets/bundle.js</code>。可以使用整个 <code>URL</code> 来作为 <code>publicPath</code>的值，如 <code>publicPath: &#39;http://localhost:8080/assets/&#39;</code>。如果你使用了 <code>HMR</code>，那么要设置 <code>publicPath</code> 就必须使用完整的 <code>URL</code></li>
</ul>
<blockquote>
<p>建议将 <code>devServer.publicPath</code> 和 <code>output.publicPath</code> 的值保持一致</p>
</blockquote>
<ul>
<li><code>proxy</code>用于配置 <code>webpack-dev-server</code>将特定 <code>URL</code> 的请求代理到另外一台服务器上。当你有单独的后端开发服务器用于请求 API 时，这个配置相当有用。例如</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">proxy: &#123;</span><br><span class="line">  <span class="string">'/api'</span>: &#123;</span><br><span class="line">    target: <span class="string">"http://localhost:3000"</span>, <span class="comment">// 将 URL 中带有 /api 的请求代理到本地的 3000 端口的服务上</span></span><br><span class="line">    pathRewrite: &#123; <span class="string">'^/api'</span>: <span class="string">''</span> &#125;, <span class="comment">// 把 URL 中 path 部分的 `api` 移除掉</span></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<ul>
<li><code>before</code> 和 <code>after</code> 配置用于在 <code>webpack-dev-server</code> 定义额外的中间件，如</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">before(app)&#123;</span><br><span class="line">  app.get(<span class="string">'/some/path'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>) </span>&#123; <span class="comment">// 当访问 /some/path 路径时，返回自定义的 json 数据</span></span><br><span class="line">    res.json(&#123; <span class="attr">custom</span>: <span class="string">'response'</span> &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<ul>
<li><code>before</code> 在 <code>webpack-dev-server</code> 静态资源中间件处理之前，可以用于拦截部分请求返回特定内容，或者实现简单的数据 <code>mock</code>。</li>
<li><code>after</code> 在 <code>webpack-dev-server</code> 静态资源中间件处理之后，比较少用到，可以用于打印日志或者做一些额外处理…</li>
</ul>
<h1 id="七、开发和生产环境的构建配置差异"><a href="#七、开发和生产环境的构建配置差异" class="headerlink" title="七、开发和生产环境的构建配置差异"></a>七、开发和生产环境的构建配置差异</h1><ul>
<li>我们在日常的前端开发工作中，一般都会有两套构建环境：一套开发时使用，构建结果用于本地开发调试，不进行代码压缩，打印 <code>debug</code> 信息，包含<code>sourcemap</code> 文件</li>
<li>另外一套构建后的结果是直接应用于线上的，即代码都是压缩后，运行时不打印 <code>debug</code> 信息，静态文件不包括 <code>sourcemap</code> 的。有的时候可能还需要多一套测试环境，在运行时直接进行请求 <code>mock</code> 等工作</li>
<li><code>webpack 4.x</code> 版本引入了 <code>mode</code> 的概念，在运行 <code>webpack</code> 时需要指定使用 <code>production</code>或 <code>development</code> 两个 <code>mode</code> 其中一个，这个功能也就是我们所需要的运行两套构建环境的能力。</li>
</ul>
<h2 id="7-1-在配置文件中区分-mode"><a href="#7-1-在配置文件中区分-mode" class="headerlink" title="7.1 在配置文件中区分 mode"></a>7.1 在配置文件中区分 mode</h2><blockquote>
<p>之前我们的配置文件都是直接对外暴露一个 <code>JS</code> 对象，这种方式暂时没有办法获取到 <code>webpack</code> 的 <code>mode</code> 参数，我们需要更换一种方式来处理配置。根据官方的文档多种配置类型，配置文件可以对外暴露一个函数，因此我们可以这样做</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">env, argv</span>) =&gt;</span> (&#123;</span><br><span class="line">  <span class="comment">// ... 其他配置</span></span><br><span class="line">  optimization: &#123;</span><br><span class="line">    minimize: <span class="literal">false</span>,</span><br><span class="line">    <span class="comment">// 使用 argv 来获取 mode 参数的值</span></span><br><span class="line">    minimizer: argv.mode === <span class="string">'production'</span> ? [</span><br><span class="line">      <span class="keyword">new</span> UglifyJsPlugin(&#123; <span class="comment">/* 你自己的配置 */</span> &#125;), </span><br><span class="line">      <span class="comment">// 仅在我们要自定义压缩配置时才需要这么做</span></span><br><span class="line">      <span class="comment">// mode 为 production 时 webpack 会默认使用压缩 JS 的 plugin</span></span><br><span class="line">    ] : [],</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这样获取 <code>mode</code> 之后，我们就能够区分不同的构建环境，然后根据不同环境再对特殊的 <code>loader</code>或<code>plugin</code> 做额外的配置就可以了</p>
</blockquote>
<ul>
<li>以上是 <code>webpack 4.x</code> 的做法，由于有了 <code>mode</code> 参数，区分环境变得简单了。不过在当前业界，估计还是使用 <code>webpack 3.x</code> 版本的居多，所以这里也简单介绍一下 <code>3.x</code> 如何区分环境</li>
</ul>
<blockquote>
<p><code>webpack</code> 的运行时环境是<code>Node.js</code>，我们可以通过 <code>Node.js</code>提供的机制给要运行的 <code>webpack</code> 程序传递环境变量，来控制不同环境下的构建行为。例如，我们在 <code>npm</code> 中的 <code>scripts</code> 字段添加一个用于生产环境的构建命令…</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"scripts"</span>: &#123;</span><br><span class="line">    <span class="string">"build"</span>: <span class="string">"NODE_ENV=production webpack"</span>,</span><br><span class="line">    <span class="string">"develop"</span>: <span class="string">"NODE_ENV=development webpack-dev-server"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>然后在 <code>webpack.config.js</code> 文件中可以通过 <code>process.env.NODE_ENV</code> 来获取命令传入的环境变量</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> config = &#123;</span><br><span class="line">  <span class="comment">// ... webpack 配置</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (process.env.NODE_ENV === <span class="string">'production'</span>) &#123;</span><br><span class="line">  <span class="comment">// 生产环境需要做的事情，如使用代码压缩插件等</span></span><br><span class="line">  config.plugins.push(<span class="keyword">new</span> UglifyJsPlugin())</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config...</span><br></pre></td></tr></table></figure>
<h2 id="7-2-运行时的环境变量"><a href="#7-2-运行时的环境变量" class="headerlink" title="7.2 运行时的环境变量"></a>7.2 运行时的环境变量</h2><blockquote>
<p>我们使用 webpack 时传递的 mode 参数，是可以在我们的应用代码运行时，通过 <code>process.env.NODE_ENV</code> 这个变量获取的。这样方便我们在运行时判断当前执行的构建环境，使用最多的场景莫过于控制是否打印 <code>debug</code> 信息…</p>
</blockquote>
<ul>
<li>下面这个简单的例子，在应用开发的代码中实现一个简单的 <code>console</code>打印封装</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">log</span>(<span class="params">...args</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (process.env.NODE_ENV === <span class="string">'development'</span> &amp;&amp; <span class="built_in">console</span> &amp;&amp; <span class="built_in">console</span>.log) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log.apply(<span class="built_in">console</span>, args)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>同样，以上是 <code>webpack 4.x</code> 的做法，下面简单介绍一下 <code>3.x</code> 版本应该如何实现。这里需要用到 <code>DefinePlugin</code> 插件，它可以帮助我们在构建时给运行时定义变量，那么我们只要在前面 <code>webpack 3.x</code> 版本区分构建环境的例子的基础上，再使用 <code>DefinePlugin</code> 添加环境变量即可影响到运行时的代码…</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="comment">// webpack 的配置</span></span><br><span class="line"></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> webpack.DefinePlugin(&#123;</span><br><span class="line">      <span class="comment">// webpack 3.x 的 process.env.NODE_ENV 是通过手动在命令行中指定 NODE_ENV=... 的方式来传递的</span></span><br><span class="line">      <span class="string">'process.env.NODE_ENV'</span>: <span class="built_in">JSON</span>.stringify(process.env.NODE_ENV),</span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h2 id="7-3-常见的环境差异配置"><a href="#7-3-常见的环境差异配置" class="headerlink" title="7.3 常见的环境差异配置"></a>7.3 常见的环境差异配置</h2><p><strong>常见的 webpack 构建差异配置</strong></p>
<ul>
<li>生产环境可能需要分离 <code>CSS</code>成单独的文件，以便多个页面共享同一个 <code>CSS</code> 文件</li>
<li>生产环境需要压缩 <code>HTML/CSS/JS</code> 代码</li>
<li>生产环境需要压缩图片</li>
<li>开发环境需要生成 <code>sourcemap</code> 文件</li>
<li>开发环境需要打印 <code>debug</code> 信息</li>
<li>开发环境需要 <code>live reload</code>或者 <code>hot reload</code> 的功能…</li>
</ul>
<blockquote>
<p><code>webpack 4.x</code> 的 <code>mode</code> 已经提供了上述差异配置的大部分功能，<code>mode</code> 为 <code>production</code> 时默认使用 <code>JS</code> 代码压缩，而<code>mode</code> 为 <code>development</code> 时默认启用 <code>hot</code> <code>reload</code>，等等。这样让我们的配置更为简洁，我们只需要针对特别使用的 <code>loader</code> 和 <code>plugin</code> 做区分配置就可以了…</p>
</blockquote>
<ul>
<li><code>webpack 3.x</code> 版本还是只能自己动手修改配置来满足大部分环境差异需求，所以如果你要开始一个新的项目，建议直接使用 <code>webpack 4.x</code>版本</li>
</ul>
<h2 id="7-4-拆分配置"><a href="#7-4-拆分配置" class="headerlink" title="7.4 拆分配置"></a>7.4 拆分配置</h2><blockquote>
<p>前面我们列出了几个环境差异配置，可能这些构建需求就已经有点多了，会让整个 <code>webpack</code> 的配置变得复杂，尤其是有着大量环境变量判断的配置。我们可以把 <code>webpack</code> 的配置按照不同的环境拆分成多个文件，运行时直接根据环境变量加载对应的配置即可。基本的划分如下…</p>
</blockquote>
<ul>
<li><code>webpack.base.js</code>：基础部分，即多个文件中共享的配置</li>
<li><code>webpack.development.js</code>：开发环境使用的配置</li>
<li><code>webpack.production.js</code>：生产环境使用的配置</li>
<li><code>webpack.test.js</code>：测试环境使用的配置…</li>
</ul>
<p><strong>如何处理这样的配置拆分</strong></p>
<blockquote>
<p>首先我们要明白，对于 <code>webpack</code> 的配置，其实是对外暴露一个 <code>JS</code> 对象，所以对于这个对象，我们都可以用 <code>JS</code> 代码来修改它，例如</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> config = &#123;</span><br><span class="line">  <span class="comment">// ... webpack 配置</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 我们可以修改这个 config 来调整配置，例如添加一个新的插件</span></span><br><span class="line">config.plugins.push(<span class="keyword">new</span> YourPlugin());</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>因此，只要有一个工具能比较智能地合并多个配置对象，我们就可以很轻松地拆分 webpack 配置，然后通过判断环境变量，使用工具将对应环境的多个配置对象整合后提供给 webpack 使用。这个工具就是 <code>webpack-merge</code></p>
</blockquote>
<ul>
<li>我们的 webpack 配置基础部分，即 <code>webpack.base.js</code> 应该大致是这样的</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: <span class="string">'...'</span>,</span><br><span class="line">  output: &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;,</span><br><span class="line">  resolve: &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    <span class="comment">// 这里是一个简单的例子，后面介绍 API 时会用到</span></span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.js$/</span>, </span><br><span class="line">        use: [<span class="string">'babel'</span>],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;,</span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>然后 <code>webpack.development.js</code> 需要添加 <code>loader</code> 或 <code>plugin</code>，就可以使用 <code>webpack-merge</code>的 <code>API</code>，例如</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; smart &#125; = <span class="built_in">require</span>(<span class="string">'webpack-merge'</span>)</span><br><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">'webpack'</span>)</span><br><span class="line"><span class="keyword">const</span> base = <span class="built_in">require</span>(<span class="string">'./webpack.base.js'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = smart(base, &#123;</span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      <span class="comment">// 用 smart API，当这里的匹配规则相同且 use 值都是数组时，smart 会识别后处理</span></span><br><span class="line">      <span class="comment">// 和上述 base 配置合并后，这里会是 &#123; test: /\.js$/, use: ['babel', 'coffee'] &#125;</span></span><br><span class="line">      <span class="comment">// 如果这里 use 的值用的是字符串或者对象的话，那么会替换掉原本的规则 use 的值</span></span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">        use: [<span class="string">'coffee'</span>],</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="comment">// ...</span></span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="comment">// plugins 这里的数组会和 base 中的 plugins 数组进行合并</span></span><br><span class="line">    <span class="keyword">new</span> webpack.DefinePlugin(&#123;</span><br><span class="line">      <span class="string">'process.env.NODE_ENV'</span>: <span class="built_in">JSON</span>.stringify(process.env.NODE_ENV),</span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;)...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>可见 <code>webpack-merge</code> 提供的 <code>smart</code> 方法，可以帮助我们更加轻松地处理 <code>loader</code> 配置的合并。<code>webpack-merge</code> 还有其他 <code>API</code> 可以用于自定义合并行为 <a href="https://github.com/survivejs/webpack-merge" target="_blank" rel="noopener">https://github.com/survivejs/webpack-merge</a></p>
</blockquote>
<h2 id="7-5-完整代码"><a href="#7-5-完整代码" class="headerlink" title="7.5 完整代码"></a>7.5 完整代码</h2><blockquote>
<p><code>webpack.config.js</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span>(<span class="params">env, argv</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> argv.mode === <span class="string">'production'</span> ?</span><br><span class="line">    <span class="built_in">require</span>(<span class="string">'./configs/webpack.production'</span>) :</span><br><span class="line">    <span class="built_in">require</span>(<span class="string">'./configs/webpack.development'</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>configs/webpack.base.js</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">const</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: <span class="string">'./src/index.js'</span>,</span><br><span class="line"></span><br><span class="line">  output: &#123;</span><br><span class="line">    path: path.resolve(__dirname, <span class="string">'../dist'</span>),</span><br><span class="line">    filename: <span class="string">'[name].js'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.jsx?/</span>,</span><br><span class="line">        include: [</span><br><span class="line">          path.resolve(__dirname, <span class="string">'../src'</span>),</span><br><span class="line">        ],</span><br><span class="line">        use: <span class="string">'babel-loader'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.(png|jpg|gif)$/</span>,</span><br><span class="line">        use: [</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'file-loader'</span></span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">      filename: <span class="string">'index.html'</span>, <span class="comment">// 配置输出文件名和路径</span></span><br><span class="line">      template: <span class="string">'src/index.html'</span>, <span class="comment">// 配置文件模板</span></span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>configs/webpack.development.js</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">'webpack'</span>)</span><br><span class="line"><span class="keyword">const</span> merge = <span class="built_in">require</span>(<span class="string">'webpack-merge'</span>)</span><br><span class="line"><span class="keyword">const</span> baseConfig = <span class="built_in">require</span>(<span class="string">'./webpack.base'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> config = merge.smart(baseConfig, &#123;</span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        enforce: <span class="string">'pre'</span>,</span><br><span class="line">        test: <span class="regexp">/\.jsx?$/</span>,</span><br><span class="line">        exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">        loader: <span class="string">"eslint-loader"</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.less$/</span>,</span><br><span class="line">        use: [</span><br><span class="line">          <span class="string">'style-loader'</span>,</span><br><span class="line">          <span class="string">'css-loader'</span>,</span><br><span class="line">          <span class="string">'less-loader'</span></span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  devServer: &#123;</span><br><span class="line">    port: <span class="string">'1234'</span>,</span><br><span class="line">    before(app)&#123;</span><br><span class="line">      app.get(<span class="string">'/api/test.json'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>) </span>&#123;</span><br><span class="line">        res.json(&#123; <span class="attr">code</span>: <span class="number">200</span>, <span class="attr">message</span>: <span class="string">'hello world'</span> &#125;)</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">config.plugins.push(</span><br><span class="line">  <span class="keyword">new</span> webpack.DefinePlugin(&#123;</span><br><span class="line">    __DEV__: <span class="built_in">JSON</span>.stringify(<span class="literal">true</span>),</span><br><span class="line">  &#125;)</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config</span><br></pre></td></tr></table></figure>
<blockquote>
<p><code>configs/webpack.production.js</code></p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> merge = <span class="built_in">require</span>(<span class="string">'webpack-merge'</span>)</span><br><span class="line"><span class="keyword">const</span> ExtractTextPlugin = <span class="built_in">require</span>(<span class="string">'extract-text-webpack-plugin'</span>)</span><br><span class="line"><span class="keyword">const</span> baseConfig = <span class="built_in">require</span>(<span class="string">'./webpack.base'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> config = merge.smart(baseConfig, &#123;</span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.less$/</span>,</span><br><span class="line">        use: ExtractTextPlugin.extract(&#123;</span><br><span class="line">          fallback: <span class="string">'style-loader'</span>,</span><br><span class="line">          use: [</span><br><span class="line">            &#123;</span><br><span class="line">              loader: <span class="string">'css-loader'</span>,</span><br><span class="line">              options: &#123;</span><br><span class="line">                minimize: <span class="literal">true</span></span><br><span class="line">              &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="string">'less-loader'</span>,</span><br><span class="line">          ],</span><br><span class="line">        &#125;),</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">config.plugins.push(<span class="keyword">new</span> ExtractTextPlugin(<span class="string">'[name].css'</span>))</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config</span><br></pre></td></tr></table></figure>
<h1 id="八、模块热替换提高开发效率"><a href="#八、模块热替换提高开发效率" class="headerlink" title="八、模块热替换提高开发效率"></a>八、模块热替换提高开发效率</h1><blockquote>
<p><code>HMR</code> 全称是 <code>Hot Module Replacement</code>，即模块热替换。在这个概念出来之前，我们使用过 <code>Hot Reloading</code>，当代码变更时通知浏览器刷新页面，以避免频繁手动刷新浏览器页面。HMR 可以理解为增强版的 <code>Hot Reloading</code>，但不用整个页面刷新，而是局部替换掉部分模块代码并且使其生效，可以看到代码变更后的效果。所以，<code>HMR</code> 既避免了频繁手动刷新页面，也减少了页面刷新时的等待，可以极大地提高前端页面开发效率…</p>
</blockquote>
<h2 id="8-1-配置使用-HMR"><a href="#8-1-配置使用-HMR" class="headerlink" title="8.1 配置使用 HMR"></a>8.1 配置使用 HMR</h2><blockquote>
<p><code>HMR</code> 是 <code>webpack</code> 提供的非常有用的一个功能，跟我们之前提到的一样，安装好 <code>webpack-dev-server</code>， 添加一些简单的配置，即在<code>webpack</code> 的配置文件中添加启用<code>HMR</code>需要的两个插件</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">'webpack'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  devServer: &#123;</span><br><span class="line">    hot: <span class="literal">true</span> <span class="comment">// dev server 的配置要启动 hot，或者在命令行中带参数开启</span></span><br><span class="line">  &#125;,</span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">new</span> webpack.NamedModulesPlugin(), <span class="comment">// 用于启动 HMR 时可以显示模块的相对路径</span></span><br><span class="line">    <span class="keyword">new</span> webpack.HotModuleReplacementPlugin(), <span class="comment">// Hot Module Replacement 的插件</span></span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h2 id="8-2-module-hot-常见的-API"><a href="#8-2-module-hot-常见的-API" class="headerlink" title="8.2 module.hot 常见的 API"></a>8.2 module.hot 常见的 API</h2><blockquote>
<p>前面 <code>HMR</code>实现部分已经讲解了实现 HMR 接口的重要性，下面来看看常见的 <code>module.hot</code> <code>API</code> 有哪些，以及如何使用</p>
</blockquote>
<ul>
<li><code>module.hot.accept</code> 方法指定在应用特定代码模块更新时执行相应的 <code>callback</code>，第一个参数可以是字符串或者数组，如</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="built_in">module</span>.hot) &#123;</span><br><span class="line">  <span class="built_in">module</span>.hot.accept([<span class="string">'./bar.js'</span>, <span class="string">'./index.css'</span>], () =&gt; &#123;</span><br><span class="line">    <span class="comment">// ... 这样当 bar.js 或者 index.css 更新时都会执行该函数</span></span><br><span class="line">  &#125;)</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<ul>
<li><code>module.hot.decline</code> 对于指定的代码模块，拒绝进行模块代码的更新，进入更新失败状态，如 <code>module.hot.decline(&#39;./bar.js&#39;)</code>。这个方法比较少用到</li>
<li><code>module.hot.dispose</code> 用于添加一个处理函数，在当前模块代码被替换时运行该函数，例如</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="built_in">module</span>.hot) &#123;</span><br><span class="line">  <span class="built_in">module</span>.hot.dispose(<span class="function">(<span class="params">data</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// data 用于传递数据，如果有需要传递的数据可以挂在 data 对象上，然后在模块代码更新后可以通过 module.hot.data 来获取</span></span><br><span class="line">  &#125;)</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<ul>
<li><code>module.hot.accept</code> 通常用于指定当前依赖的某个模块更新时需要做的处理，如果是当前模块更新时需要处理的动作，使用 <code>module.hot.dispose</code> 会更加容易方便</li>
<li><code>module.hot.removeDisposeHandler</code>用于移除 <code>dispose</code> 方法添加的 <code>callback</code></li>
</ul>
<h1 id="九、图片加载优化"><a href="#九、图片加载优化" class="headerlink" title="九、图片加载优化"></a>九、图片加载优化</h1><h2 id="9-1-CSS-Sprites"><a href="#9-1-CSS-Sprites" class="headerlink" title="9.1 CSS Sprites"></a>9.1 CSS Sprites</h2><ul>
<li>如果你使用的 <code>webpack 3.x</code> 版本，需要 <code>CSS Sprites</code> 的话，可以使用 <code>webpack-spritesmith</code> 或者 <code>sprite-webpack-plugin</code>。</li>
<li>我们以 <code>webpack-spritesmith</code> 为例，先安装依赖…</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>: &#123;</span><br><span class="line">  loaders: [</span><br><span class="line">    <span class="comment">// ... 这里需要有处理图片的 loader，如 file-loader</span></span><br><span class="line">  ]</span><br><span class="line">&#125;,</span><br><span class="line">resolve: &#123;</span><br><span class="line">  modules: [</span><br><span class="line">    <span class="string">'node_modules'</span>, </span><br><span class="line">    <span class="string">'spritesmith-generated'</span>, <span class="comment">// webpack-spritesmith 生成所需文件的目录</span></span><br><span class="line">  ],</span><br><span class="line">&#125;,</span><br><span class="line">plugins: [</span><br><span class="line">  <span class="keyword">new</span> SpritesmithPlugin(&#123;</span><br><span class="line">    src: &#123;</span><br><span class="line">      cwd: path.resolve(__dirname, <span class="string">'src/ico'</span>), <span class="comment">// 多个图片所在的目录</span></span><br><span class="line">      glob: <span class="string">'*.png'</span> <span class="comment">// 匹配图片的路径</span></span><br><span class="line">    &#125;,</span><br><span class="line">    target: &#123;</span><br><span class="line">      <span class="comment">// 生成最终图片的路径</span></span><br><span class="line">      image: path.resolve(__dirname, <span class="string">'src/spritesmith-generated/sprite.png'</span>), </span><br><span class="line">      <span class="comment">// 生成所需 SASS/LESS/Stylus mixins 代码，我们使用 Stylus 预处理器做例子</span></span><br><span class="line">      css: path.resolve(__dirname, <span class="string">'src/spritesmith-generated/sprite.styl'</span>), </span><br><span class="line">    &#125;,</span><br><span class="line">    apiOptions: &#123;</span><br><span class="line">      cssImageRef: <span class="string">"~sprite.png"</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;),</span><br><span class="line">],...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>在你需要的样式代码中引入 <code>sprite.styl</code> 后调用需要的<code>mixins</code> 即可</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">@import &apos;~sprite.styl&apos;</span><br><span class="line"></span><br><span class="line">.close-button</span><br><span class="line">    sprite($close)</span><br><span class="line">.open-button</span><br><span class="line">    sprite($open)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果你使用的是 <code>webpack 4.x</code>，你需要配合使用 <code>postcss</code>和 <code>postcss-sprites</code>，才能实现 <code>CSS Sprites</code> 的相关构建</p>
</blockquote>
<h2 id="9-2-图片压缩"><a href="#9-2-图片压缩" class="headerlink" title="9.2 图片压缩"></a>9.2 图片压缩</h2><ul>
<li>在一般的项目中，图片资源会占前端资源的很大一部分，既然代码都进行压缩了，占大头的图片就更不用说了</li>
<li>我们之前提及使用<code>file-loader</code> 来处理图片文件，在此基础上，我们再添加一个 <code>image-webpack-loader</code>来压缩图片文件。简单的配置如下…</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/.*\.(gif|png|jpe?g|svg|webp)$/i</span>,</span><br><span class="line">        use: [</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'file-loader'</span>,</span><br><span class="line">            options: &#123;&#125;</span><br><span class="line">          &#125;,</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'image-webpack-loader'</span>,</span><br><span class="line">            options: &#123;</span><br><span class="line">              mozjpeg: &#123; <span class="comment">// 压缩 jpeg 的配置</span></span><br><span class="line">                progressive: <span class="literal">true</span>,</span><br><span class="line">                quality: <span class="number">65</span></span><br><span class="line">              &#125;,</span><br><span class="line">              optipng: &#123; <span class="comment">// 使用 imagemin-optipng 压缩 png，enable: false 为关闭</span></span><br><span class="line">                enabled: <span class="literal">false</span>,</span><br><span class="line">              &#125;,</span><br><span class="line">              pngquant: &#123; <span class="comment">// 使用 imagemin-pngquant 压缩 png</span></span><br><span class="line">                quality: <span class="string">'65-90'</span>,</span><br><span class="line">                speed: <span class="number">4</span></span><br><span class="line">              &#125;,</span><br><span class="line">              gifsicle: &#123; <span class="comment">// 压缩 gif 的配置</span></span><br><span class="line">                interlaced: <span class="literal">false</span>,</span><br><span class="line">              &#125;,</span><br><span class="line">              webp: &#123; <span class="comment">// 开启 webp，会把 jpg 和 png 图片压缩为 webp 格式</span></span><br><span class="line">                quality: <span class="number">75</span></span><br><span class="line">              &#125;,</span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h2 id="9-3-使用-DataURL"><a href="#9-3-使用-DataURL" class="headerlink" title="9.3 使用 DataURL"></a>9.3 使用 DataURL</h2><blockquote>
<p>有的时候我们的项目中会有一些很小的图片，因为某些缘故并不想使用 <code>CSS Sprites</code> 的方式来处理（譬如小图片不多，因此引入 CSS Sprites 感觉麻烦），那么我们可以在 webpack 中使用 <code>url-loader</code> 来处理这些很小的图片…</p>
</blockquote>
<ul>
<li><code>url-loader</code> 和 <code>file-loader</code> 的功能类似，但是在处理文件的时候，可以通过配置指定一个大小，当文件小于这个配置值时，<code>url-loader</code> 会将其转换为一个 <code>base64</code> 编码的 <code>DataURL</code>，配置如下</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.(png|jpg|gif)$/</span>,</span><br><span class="line">        use: [</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'url-loader'</span>,</span><br><span class="line">            options: &#123;</span><br><span class="line">              limit: <span class="number">8192</span>, <span class="comment">// 单位是 Byte，当文件小于 8KB 时作为 DataURL 处理</span></span><br><span class="line">            &#125;,</span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<h2 id="9-4-代码压缩"><a href="#9-4-代码压缩" class="headerlink" title="9.4 代码压缩"></a>9.4 代码压缩</h2><ul>
<li><code>webpack 4.x</code> 版本运行时，<code>mode</code> 为 <code>production</code> 即会启动压缩 <code>JS</code> 代码的插件，而对于 <code>webpack</code> <code>3.x</code>，使用压缩 <code>JS</code> 代码插件的方式也已经介绍过了。在生产环境中，压缩 <code>JS</code> 代码基本是一个必不可少的步骤，这样可以大大减小 <code>JavaScript</code> 的体积，相关内容这里不再赘述。</li>
<li>除了 JS 代码之外，我们一般还需要 HTML 和 CSS 文件，这两种文件也都是可以压缩的，虽然不像 JS 的压缩那么彻底（替换掉长变量等），只能移除空格换行等无用字符，但也能在一定程度上减小文件大小。在 webpack 中的配置使用也不是特别麻烦，所以我们通常也会使用。</li>
<li>对于 HTML 文件，之前介绍的 <code>html-webpack-plugin</code> 插件可以帮助我们生成需要的 HTML 并对其进行压缩…</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">      filename: <span class="string">'index.html'</span>, <span class="comment">// 配置输出文件名和路径</span></span><br><span class="line">      template: <span class="string">'assets/index.html'</span>, <span class="comment">// 配置文件模板</span></span><br><span class="line">      minify: &#123; <span class="comment">// 压缩 HTML 的配置</span></span><br><span class="line">        minifyCSS: <span class="literal">true</span>, <span class="comment">// 压缩 HTML 中出现的 CSS 代码</span></span><br><span class="line">        minifyJS: <span class="literal">true</span> <span class="comment">// 压缩 HTML 中出现的 JS 代码</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<ul>
<li>如上，使用 <code>minify</code> 字段配置就可以使用 <code>HTML</code> 压缩，这个插件是使用 <code>html-minifier</code> 来实现<code>HTML</code> 代码压缩的，<code>minify</code>下的配置项直接透传给 <code>html-minifier</code>，配置项参考 <code>html-minifier</code> 文档即可。</li>
<li>对于 CSS 文件，我们之前介绍过用来处理 CSS 文件的 <code>css-loader</code>，也提供了压缩 CSS 代码的功能：…</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      <span class="comment">// ...</span></span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.css/</span>,</span><br><span class="line">        include: [</span><br><span class="line">          path.resolve(__dirname, <span class="string">'src'</span>),</span><br><span class="line">        ],</span><br><span class="line">        use: [</span><br><span class="line">          <span class="string">'style-loader'</span>,</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'css-loader'</span>,</span><br><span class="line">            options: &#123;</span><br><span class="line">              minimize: <span class="literal">true</span>, <span class="comment">// 使用 css 的压缩功能</span></span><br><span class="line">            &#125;,</span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>在 <code>css-loader</code> 的选项中配置 <code>minimize</code> 字段为 <code>true</code>来使用<code>CSS</code> 压缩代码的功能。<code>css-loader</code> 是使用 <code>cssnano</code>来压缩代码的，<code>minimize</code> 字段也可以配置为一个对象，来将相关配置传递给 <code>cssnano</code>…</p>
</blockquote>
<h1 id="十、分离代码文件"><a href="#十、分离代码文件" class="headerlink" title="十、分离代码文件"></a>十、分离代码文件</h1><ul>
<li>关于分离 CSS 文件这个主题，之前在介绍如何搭建基本的前端开发环境时有提及，在 <code>webpack</code> 中使用 <code>extract-text-webpack-plugin</code> 插件即可。</li>
<li>先简单解释一下为何要把 CSS 文件分离出来，而不是直接一起打包在 JS 中。最主要的原因是我们希望更好地利用缓存。</li>
<li>假设我们原本页面的静态资源都打包成一个 JS 文件，加载页面时虽然只需要加载一个 JS 文件，但是我们的代码一旦改变了，用户访问新的页面时就需要重新加载一个新的 JS 文件。有些情况下，我们只是单独修改了样式，这样也要重新加载整个应用的 JS 文件，相当不划算。</li>
<li>还有一种情况是我们有多个页面，它们都可以共用一部分样式（这是很常见的，CSS Reset、基础组件样式等基本都是跨页面通用），如果每个页面都单独打包一个 JS 文件，那么每次访问页面都会重复加载原本可以共享的那些 CSS 代码。如果分离开来，第二个页面就有了 CSS 文件的缓存，访问速度自然会加快。虽然对第一个页面来说多了一个请求，但是对随后的页面来说，缓存带来的速度提升相对更加可观…</li>
</ul>
<blockquote>
<p><code>3.x</code> 以前的版本是使用 <code>CommonsChunkPlugin</code> 来做代码分离的，而 <code>webpack 4.x</code> 则是把相关的功能包到了<code>optimize.splitChunks</code> 中，直接使用该配置就可以实现代码分离。</p>
</blockquote>
<h2 id="10-1-webpack-4-x-的-optimization"><a href="#10-1-webpack-4-x-的-optimization" class="headerlink" title="10.1 webpack 4.x 的 optimization"></a>10.1 webpack 4.x 的 optimization</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ... webpack 配置</span></span><br><span class="line"></span><br><span class="line">  optimization: &#123;</span><br><span class="line">    splitChunks: &#123;</span><br><span class="line">      chunks: <span class="string">"all"</span>, <span class="comment">// 所有的 chunks 代码公共的部分分离出来成为一个单独的文件</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>我们需要在 HTML 中引用两个构建出来的 JS 文件，并且 <code>commons.js</code> 需要在入口代码之前。下面是个简单的例子</p>
</blockquote>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"commons.js"</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"entry.bundle.js"</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果你使用了 <code>html-webpack-plugin</code>，那么对应需要的 JS 文件都会在 HTML 文件中正确引用，不用担心。如果没有使用，那么你需要从 <code>stats</code> 的 <code>entrypoints</code> 属性来获取入口应该引用哪些 JS 文件，可以参考 Node API 了解如何从 stats 中获取信息…</p>
</blockquote>
<p><strong>显式配置共享类库可以这么操作</strong></p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: &#123;</span><br><span class="line">    vendor: [<span class="string">"react"</span>, <span class="string">"lodash"</span>, <span class="string">"angular"</span>, ...], <span class="comment">// 指定公共使用的第三方类库</span></span><br><span class="line">  &#125;,</span><br><span class="line">  optimization: &#123;</span><br><span class="line">    splitChunks: &#123;</span><br><span class="line">      cacheGroups: &#123;</span><br><span class="line">        vendor: &#123;</span><br><span class="line">          chunks: <span class="string">"initial"</span>,</span><br><span class="line">          test: <span class="string">"vendor"</span>,</span><br><span class="line">          name: <span class="string">"vendor"</span>, <span class="comment">// 使用 vendor 入口作为公共部分</span></span><br><span class="line">          enforce: <span class="literal">true</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ... 其他配置</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  optimization: &#123;</span><br><span class="line">    splitChunks: &#123;</span><br><span class="line">      cacheGroups: &#123;</span><br><span class="line">        vendor: &#123;</span><br><span class="line">          test: <span class="regexp">/react|angluar|lodash/</span>, <span class="comment">// 直接使用 test 来做路径匹配</span></span><br><span class="line">          chunks: <span class="string">"initial"</span>,</span><br><span class="line">          name: <span class="string">"vendor"</span>,</span><br><span class="line">          enforce: <span class="literal">true</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  optimization: &#123;</span><br><span class="line">    splitChunks: &#123;</span><br><span class="line">      cacheGroups: &#123;</span><br><span class="line">        vendor: &#123;</span><br><span class="line">          chunks: <span class="string">"initial"</span>,</span><br><span class="line">          test: path.resolve(__dirname, <span class="string">"node_modules"</span>) <span class="comment">// 路径在 node_modules 目录下的都作为公共部分</span></span><br><span class="line">          name: <span class="string">"vendor"</span>, <span class="comment">// 使用 vendor 入口作为公共部分</span></span><br><span class="line">          enforce: <span class="literal">true</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上述第一种做法是显示指定哪些类库作为公共部分，第二种做法实现的功能差不多，只是利用了 test 来做模块路径的匹配，第三种做法是把所有在 node_modules 下的模块，即作为依赖安装的，都作为公共部分。你可以针对项目情况，选择最合适的做法..</p>
</blockquote>
<h2 id="10-2-webpack-3-x-的-CommonsChunkPlugin"><a href="#10-2-webpack-3-x-的-CommonsChunkPlugin" class="headerlink" title="10.2 webpack 3.x 的 CommonsChunkPlugin"></a>10.2 webpack 3.x 的 CommonsChunkPlugin</h2><blockquote>
<p><code>webpack 3.x</code>以下的版本需要用到 webpack 自身提供的 <code>CommonsChunkPlugin</code> 插件。我们先来看一个最简单的例子</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> webpack.optimize.CommonsChunkPlugin(&#123;</span><br><span class="line">      name: <span class="string">"commons"</span>, <span class="comment">// 公共使用的 chunk 的名称</span></span><br><span class="line">      filename: <span class="string">"commons.js"</span>, <span class="comment">// 公共 chunk 的生成文件名</span></span><br><span class="line">      minChunks: <span class="number">3</span>, <span class="comment">// 公共的部分必须被 3 个 chunk 共享</span></span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<ul>
<li><code>chunk</code> 在这里是构建的主干，可以简单理解为一个入口对应一个 <code>chunk</code>。</li>
<li>以上插件配置在构建后会生成一个 <code>commons.js</code> 文件，该文件就是代码中的公共部分。上面的配置中 <code>minChunks</code>字段为 3，该字段的意思是当一个模块被 3 个以上的 <code>chunk</code> 依赖时，这个模块就会被划分到 <code>commons chunk</code> 中去。单从这个配置的角度上讲，这种方式并没有 <code>4.x</code> 的 <code>chunks: &quot;all&quot;</code>那么方便。</li>
</ul>
<p><strong>CommonsChunkPlugin 也是支持显式配置共享类库的</strong></p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: &#123;</span><br><span class="line">    vendor: [<span class="string">'react'</span>, <span class="string">'react-redux'</span>], <span class="comment">// 指定公共使用的第三方类库</span></span><br><span class="line">    app: <span class="string">'./src/entry'</span>,</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> webpack.optimize.CommonsChunkPlugin(&#123;</span><br><span class="line">      name: <span class="string">'vendor'</span> <span class="comment">// 使用 vendor 入口作为公共部分</span></span><br><span class="line">      filename: <span class="string">"vendor.js"</span>, </span><br><span class="line">      minChunks: <span class="literal">Infinity</span>, <span class="comment">// 这个配置会让 webpack 不再自动抽离公共模块</span></span><br><span class="line">    &#125;),</span><br><span class="line">  ],</span><br><span class="line">&#125;...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>上述配置会生成一个名为 <code>vendor.js</code> 的共享代码文件，里面包含了 <code>React</code> 和<code>React-Redux</code> 库的代码，可以提供给多个不同的入口代码使用。这里的 <code>minChunks</code> 字段的配置，我们使用了 <code>Infinity</code>，可以理解为<code>webpack</code> 不自动抽离公共模块。如果这里和之前一样依旧设置为 3，那么被 3 个以上的<code>chunk</code>依赖的模块会和 <code>React</code>、<code>React-Redux</code> 一同打包进 <code>vendor</code>，这样就失去显式指定的意义了。</p>
</blockquote>
<blockquote>
<p><code>minChunks</code>其实还可以是一个函数，如：</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">minChunks: <span class="function">(<span class="params"><span class="built_in">module</span>, count</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="built_in">module</span>, count);</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>
<blockquote>
<p>该函数在分析每一个依赖的时候会被调用，传入当前依赖模块的信息 <code>module</code>，以及已经被作为公共模块的数量 <code>count</code>，你可以在函数中针对每一个模块做更加精细化的控制。看一个简单的例子：</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">minChunks: <span class="function">(<span class="params"><span class="built_in">module</span>, count</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">module</span>.context &amp;&amp; <span class="built_in">module</span>.context.includes(<span class="string">"node_modules"</span>); </span><br><span class="line">  <span class="comment">// node_modules 目录下的模块都作为公共部分，效果就如同 webpack 4.x 中的 test: path.resolve(__dirname, "node_modules")</span></span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>
<ul>
<li>更多使用 <code>CommonsChunkPlugin</code>的配置参考官方文档 <code>commons-chunk-plugin</code>。</li>
</ul>
<h1 id="十一、进一步控制JS大小"><a href="#十一、进一步控制JS大小" class="headerlink" title="十一、进一步控制JS大小"></a>十一、进一步控制JS大小</h1><h2 id="11-1-按需加载模块"><a href="#11-1-按需加载模块" class="headerlink" title="11.1 按需加载模块"></a>11.1 按需加载模块</h2><blockquote>
<p>在 webpack 的构建环境中，要按需加载代码模块很简单，遵循 ES 标准的动态加载语法 <code>dynamic-import</code> 来编写代码即可，<code>webpack</code> 会自动处理使用该语法编写的模块</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// import 作为一个方法使用，传入模块名即可，返回一个 promise 来获取模块暴露的对象</span></span><br><span class="line"><span class="comment">// 注释 webpackChunkName: "lodash" 可以用于指定 chunk 的名称，在输出文件时有用</span></span><br><span class="line"><span class="keyword">import</span>(<span class="comment">/* webpackChunkName: "lodash" */</span> <span class="string">'lodash'</span>).then(<span class="function">(<span class="params">_</span>) =&gt;</span> &#123; </span><br><span class="line">  <span class="built_in">console</span>.log(_.lash([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])) <span class="comment">// 打印 3</span></span><br><span class="line">&#125;)...</span><br></pre></td></tr></table></figure>
<ul>
<li>注意一下，如果你使用了 <code>Babel</code> 的话，还需要 <code>Syntax Dynamic Import</code> 这个 <code>Babel</code> 插件来处理 <code>import()</code> 这种语法。</li>
<li>由于动态加载代码模块的语法依赖于 <code>promise</code>，对于低版本的浏览器，需要添加 <code>promise</code> 的 <code>polyfill</code> 后才能使用。</li>
<li>如上的代码，webpack 构建时会自动把 <code>lodash</code> 模块分离出来，并且在代码内部实现动态加载 <code>lodash</code> 的功能。动态加载代码时依赖于网络，其模块内容会异步返回，所以 import 方法是返回一个 <code>promise</code> 来获取动态加载的模块内容。</li>
<li><code>import</code> 后面的注释 <code>webpackChunkName: &quot;lodash&quot;</code> 用于告知 <code>webpack</code>所要动态加载模块的名称。我们在 webpack 配置中添加一个 <code>output.chunkFilename</code> 的配置…</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">output: &#123;</span><br><span class="line">  path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">  filename: <span class="string">'[name].[hash:8].js'</span>,</span><br><span class="line">  chunkFilename: <span class="string">'[name].[hash:8].js'</span> <span class="comment">// 指定分离出来的代码文件的名称</span></span><br><span class="line">&#125;,...</span><br></pre></td></tr></table></figure>
<blockquote>
<p>这样就可以把分离出来的文件名称用 lodash 标识了，如下图：</p>
</blockquote>
<p><img src="https://upload-images.jianshu.io/upload_images/1480597-ae36b6816feed422.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p>
<blockquote>
<p>如果没有添加注释 <code>webpackChunkName: &quot;lodash&quot; 以及 output.chunkFilename</code> 配置，那么分离出来的文件名称会以简单数字的方式标识，不便于识别</p>
</blockquote>
<h2 id="11-2-以上完整示例代码"><a href="#11-2-以上完整示例代码" class="headerlink" title="11.2 以上完整示例代码"></a>11.2 以上完整示例代码</h2><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">'webpack'</span>)</span><br><span class="line"><span class="keyword">const</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>)</span><br><span class="line"><span class="keyword">const</span> ExtractTextPlugin = <span class="built_in">require</span>(<span class="string">'extract-text-webpack-plugin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: <span class="string">'./src/index.js'</span>,</span><br><span class="line"></span><br><span class="line">  output: &#123;</span><br><span class="line">    path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line">    filename: <span class="string">'[name].js'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.jsx?/</span>,</span><br><span class="line">        include: [</span><br><span class="line">          path.resolve(__dirname, <span class="string">'src'</span>),</span><br><span class="line">        ],</span><br><span class="line">        use: <span class="string">'babel-loader'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.less$/</span>,</span><br><span class="line">        use: ExtractTextPlugin.extract(&#123;</span><br><span class="line">          fallback: <span class="string">'style-loader'</span>,</span><br><span class="line">          use: [</span><br><span class="line">            <span class="string">'css-loader'</span>,</span><br><span class="line">            <span class="string">'postcss-loader'</span>,</span><br><span class="line">            <span class="string">'less-loader'</span>,</span><br><span class="line">          ],</span><br><span class="line">        &#125;),</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        test: <span class="regexp">/\.(png|jpg|gif)$/</span>,</span><br><span class="line">        use: [</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'url-loader'</span>,</span><br><span class="line">            options: &#123;</span><br><span class="line">              limit: <span class="number">8192</span></span><br><span class="line">            &#125;,</span><br><span class="line">          &#125;,</span><br><span class="line">          &#123;</span><br><span class="line">            loader: <span class="string">'image-webpack-loader'</span>,</span><br><span class="line">            options: &#123;</span><br><span class="line">              mozjpeg: &#123; <span class="comment">// 压缩 jpeg 的配置</span></span><br><span class="line">                progressive: <span class="literal">true</span>,</span><br><span class="line">                quality: <span class="number">65</span></span><br><span class="line">              &#125;,</span><br><span class="line">              optipng: &#123; <span class="comment">// 使用 imagemin-optipng 压缩 png，enable: false 为关闭</span></span><br><span class="line">                enabled: <span class="literal">false</span>,</span><br><span class="line">              &#125;,</span><br><span class="line">              pngquant: &#123; <span class="comment">// 使用 imagemin-pngquant 压缩 png</span></span><br><span class="line">                quality: <span class="string">'65-90'</span>,</span><br><span class="line">                speed: <span class="number">4</span></span><br><span class="line">              &#125;,</span><br><span class="line">              gifsicle: &#123; <span class="comment">// 压缩 gif 的配置</span></span><br><span class="line">                interlaced: <span class="literal">false</span>,</span><br><span class="line">              &#125;,</span><br><span class="line">              webp: &#123; <span class="comment">// 开启 webp，会把 jpg 和 png 图片压缩为 webp 格式</span></span><br><span class="line">                quality: <span class="number">75</span></span><br><span class="line">              &#125;,</span><br><span class="line">            &#125;,</span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;,</span><br><span class="line">    ],</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  optimization: &#123;</span><br><span class="line">    splitChunks: &#123;</span><br><span class="line">      cacheGroups: &#123;</span><br><span class="line">        vendor: &#123;</span><br><span class="line">          chunks: <span class="string">"initial"</span>,</span><br><span class="line">          test: path.resolve(__dirname, <span class="string">"node_modules"</span>), <span class="comment">// 路径在 node_modules 目录下的都作为公共部分</span></span><br><span class="line">          name: <span class="string">"vendor"</span>, <span class="comment">// 使用 vendor 入口作为公共部分</span></span><br><span class="line">          enforce: <span class="literal">true</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> HtmlWebpackPlugin(&#123;</span><br><span class="line">      filename: <span class="string">'index.html'</span>, <span class="comment">// 配置输出文件名和路径</span></span><br><span class="line">      template: <span class="string">'src/index.html'</span>, <span class="comment">// 配置文件模板</span></span><br><span class="line">      minify: &#123; <span class="comment">// 压缩 HTML 的配置</span></span><br><span class="line">        minifyCSS: <span class="literal">true</span>, <span class="comment">// 压缩 HTML 中出现的 CSS 代码</span></span><br><span class="line">        minifyJS: <span class="literal">true</span>, <span class="comment">// 压缩 HTML 中出现的 JS 代码</span></span><br><span class="line">        removeComments: <span class="literal">true</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;),</span><br><span class="line">    <span class="keyword">new</span> ExtractTextPlugin(<span class="string">'[name].css'</span>),</span><br><span class="line">    <span class="keyword">new</span> webpack.NamedModulesPlugin(),</span><br><span class="line">    <span class="keyword">new</span> webpack.HotModuleReplacementPlugin(),</span><br><span class="line">  ],</span><br><span class="line"></span><br><span class="line">  devServer: &#123;</span><br><span class="line">    hot: <span class="literal">true</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

      </div>
    
  </div>

</article>

<button class="assist-btn2 circle" id="assist_btn2" title="点亮屏幕" style="left: 27px; top: 152px;">
  <i class="iconfont" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>
<button class="assist-btn1 circle" id="assist_btn1" title="关闭屏幕亮度" style="left: 27px; top: 152px;">
  <i class="iconfont toc-title" style="display:inline-block;color:red;width:20px;height:20px;">&#xe61d;</i>
</button>


<script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>	

<script src="https://my.openwrite.cn/js/readmore.js" type="text/javascript"></script>
<script>
  const btw = new BTWPlugin();
  btw.init({
    id: "container",
    blogId: "22699-1592137983091-414",
    name: "前端进阶之旅",
    qrcode: "https://poetries1.gitee.io/img-repo/2020/06/qrcode.jpg",
    keyword: "3a3b3c",
  });
</script>

<script type="text/javascript">

// white theme
var body = {color: "#555", background: "#000"};
var a_tag = {color: "#222"};
var header = { background: "#222"};
var logo_line_i = {background: "#222"};
// var post_code = {background: "#eee", color: "#222"};

function switch_theme() {
 $("body").css(body);
 $("a:not('.links-of-author-item a, .site-state-item a, .site-state-posts a, .feed-link a, .motion-element a, .post-tags a, .show-commit-cls a, #donate_board a')").css(a_tag);
 $(".header, .footer").css(header);
 $(".logo-line-before i, .logo-line-after i").css(logo_line_i);
 //$(".post code").css(post_code);
 $("#idhyt-surprise-ball #idhyt-surprise-ball-animation .drag").css(a_tag);
 $(".post-title-link, .posts-expand .post-meta, .post-comments-count, .disqus-comment-count, .post-category a, .post-nav-next a, .post-nav-item a").css(a_tag);
 
 // $("code").css({color: '#c5c8c6', background: '#1d1f21'});
 //$("#assist_btn1").hide(1500);
}

$(function () {
$("#assist_btn2").css("display","none");
 $("#assist_btn1").click(function() {
     switch_theme();
$("div#toc.toc-article").css({
 "background":"#eaeaea",
 "opacity":1
});
$(".toc-article ol").show();
$("#toc.toc-article .toc-title").css("color","#a98602");
$("#assist_btn1").css("display","none");
$("#assist_btn2").css("display","block");
 });
$("#assist_btn2").click(function() {
$("#assist_btn2").css("display","none");
$("#assist_btn1").css("display","block");
$("body").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
     $(".header, .footer").css("background","url(http://www.miaov.com/static/ie/images/news/bg.png)")
$(".toc-article ol").toggle(1000);
 });
});


//背景随机

var Y, O, E, L, B, C, T, z, N, S, A, I;
!function() {
var e = function() {
for (O.clearRect(0, 0, L, B), T = [{
x: 0,
y: .7 * B + C
}, {
x: 0,
y: .7 * B - C
}]; T[1].x < L + C;) t(T[0], T[1])
}, t = function(e, t) {
O.beginPath(), O.moveTo(e.x, e.y), O.lineTo(t.x, t.y);
var n = t.x + (2 * I() - .25) * C,
 r = a(t.y);
O.lineTo(n, r), O.closePath(), N -= S / -50, O.fillStyle = "#" + (127 * A(N) + 128 << 16 | 127 * A(N + S / 3) + 128 << 8 | 127 * A(N + S / 3 * 2) + 128).toString(16), O.fill(), T[0] = T[1], T[1] = {
 x: n,
 y: r
}
}, a = function n(e) {
var t = e + (2 * I() - 1.1) * C;
return t > B || t < 0 ? n(e) : t
};
Y = document.getElementById("evanyou"), O = Y.getContext("2d"), E = window.devicePixelRatio || 1, L = window.innerWidth, B = window.innerHeight, C = 90, z = Math, N = 0, S = 2 * z.PI, A = z.cos, I = z.random, Y.width = L * E, Y.height = B * E, O.scale(E, E), O.globalAlpha = .6, document.onclick = e, document.ontouchstart = e, e()
}()

   
$("#toc-eye").click(function(){
$("#toc.toc-article").toggle(1000);
});

</script>


   
  <div class="text-center donation">
    <div class="inner-donation">
      <span class="btn-donation">支持一下</span>
      <div class="donation-body">
        <div class="tip text-center">扫一扫，支持poetries</div>
        <ul>
        
          <li class="item">
            
              <span>微信扫一扫</span>
            
            <img src="/images/weixin.jpg" alt="">
          </li>
        
          <li class="item">
            
              <span>支付宝扫一扫</span>
            
            <img src="/images/zhifubao.jpg" alt="">
          </li>
        
        </ul>
      </div>
    </div>
  </div>


   
  <div class="box-prev-next clearfix">
    <a class="show pull-left" href="/2018/09/03/wx-taro/">
        <i class="icon icon-angle-left"></i>
    </a>
    <a class="show pull-right" href="/2018/09/04/pwa/">
        <i class="icon icon-angle-right"></i>
    </a>
  </div>




</div>


  <a id="backTop" class="back-top">
    <i class="icon-angle-up"></i>
  </a>




  <div class="modal" id="modal">
  <span id="cover" class="cover hide"></span>
  <div id="modal-dialog" class="modal-dialog hide-dialog">
    <div class="modal-header">
      <span id="close" class="btn-close">关闭</span>
    </div>
    <hr>
    <div class="modal-body">
      <ul class="list-toolbox">
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/archives/"
              rel="noopener noreferrer"
              target="_self"
              >
              博客
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/categories/"
              rel="noopener noreferrer"
              target="_self"
              >
              分类
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/tags/"
              rel="noopener noreferrer"
              target="_self"
              >
              标签
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/search/"
              rel="noopener noreferrer"
              target="_self"
              >
              搜索
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/link/"
              rel="noopener noreferrer"
              target="_self"
              >
              友链
            </a>
          </li>
        
          <li class="item-toolbox">
            <a
              class="CIRCLE"
              href="/about/"
              rel="noopener noreferrer"
              target="_self"
              >
              关于
            </a>
          </li>
        
      </ul>

    </div>
  </div>
</div>



  
      <div class="fexo-comments comments-post">
    

    

    
    

    

    
    

    

<!-- Gitalk评论插件通用代码 -->
<div id="gitalk-container"></div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<script src="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js"></script>
<script>
const gitalk = new Gitalk({
  clientID: '5567a2c4abb858009d96',
  clientSecret: 'b9039ec056cf5c2346b3cdb63308a28c163f91e5',
  repo: 'poetries.github.io',
  owner: 'poetries',
  // 在这里设置一下截取前50个字符串, 这是因为 github 对 label 的长度有了要求, 如果超过
  // 50个字符串则会报错.
  // id: location.pathname.split('/').pop().substring(0, 49),
  id: location.pathname,
  admin: ['poetries'],
  // facebook-like distraction free mode
  distractionFreeMode: false
})
gitalk.render('gitalk-container')
</script>
<!-- Gitalk代码结束 -->



  </div>

  

  <script type="text/javascript">
  function loadScript(url, callback) {
    var script = document.createElement('script')
    script.type = 'text/javascript';

    if (script.readyState) { //IE
      script.onreadystatechange = function() {
        if (script.readyState == 'loaded' ||
          script.readyState == 'complete') {
          script.onreadystatechange = null;
          callback();
        }
      };
    } else { //Others
      script.onload = function() {
        callback();
      };
    }

    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  window.onload = function() {
    loadScript('/js/bundle.js?235683', function() {
      // load success
    });
  }
</script>


  <!-- 页面点击小红心 -->
  <script type="text/javascript" src="/js/clicklove.js"></script>
 
  
</body>
</html>
